Learn a design pattern every day: behavioral command pattern

1, Basic concepts

Command mode is a highly cohesive mode, which is defined as: encapsulate a request as an object, thereby writing your parameterized clients with different requests, queue or log requests, Encapsulate a request into an object, so that you can use different requests to parameterize the client, queue the request or record the request log, and provide command revocation and recovery functions

2, Popular explanation

COMMAND command mode: I have a MM who is very strict at home and can't meet, so I have to rely on her brother to transmit information between us. If she has any instructions for me, she will write a note for her brother to bring to me. No, her brother sent him another COMMAND. To thank him, I asked him to eat a bowl of noodles with mixed sauce. He said, "I sent commands to my sister's three boyfriends at the same time, and I only asked me to eat noodles after counting your stinginess." COMMAND mode: COMMAND mode encapsulates a request or operation into an object. The COMMAND mode divides the responsibility of issuing and executing commands, and delegates them to different objects. The COMMAND mode allows the requesting party to be independent from the sending party, so that the requesting party does not have to know the interface of the receiving party, how the request is received, and whether, when and how the operation is executed. The system supports undo of commands.

3, Command mode explanation

1. General template of command mode

The general class diagram of the command pattern is shown in the figure.

In this class diagram, we see three roles:

● Receive recipient role

This role is the role of work. The command passed here should be executed. In the above example, there are three implementation classes of Group.

● Command command role

All commands that need to be executed are declared here.

● Invoker caller role

Receive command and execute command. In the example, I (Project Manager) is the role.

The command mode is relatively simple, but it is used very frequently in the project, because it has a very good encapsulation. It separates the requester and the Receiver, and has a good guarantee of scalability. The general code is relatively simple. Let's read the Receiver class first.

public abstract class Receiver {
    /**
     * Abstract receiver, which defines the business that each receiver must complete
     */
    public abstract void doSomething();
}

Strange, why is Receiver an abstract class? That's because there can be multiple receivers. If there are multiple receivers, you need to define an abstract set of all characteristics - Abstract receivers. The specific receivers are as follows:

public class ConcreteReceiver1 extends Receiver {
    @Override
    public void doSomething() {
        //Business logic
    }
}

public class ConcreteReceiver2 extends Receiver {
    @Override
    public void doSomething() {
        //Business logic
    }
}

The recipients can be N, which depends on the specific definition of the business. Command role is the core of command mode, and its abstract command classes are as follows:

public abstract class Command {
    /**
     * Each command class must have a method to execute the command
     */
    public abstract void execute();
}

According to the requirements of the environment, there can also be N specific command classes, and their implementation classes are as follows:

public class ConcreteCommand1 extends Command {
    /**
     * Which Receiver class to command
     */
    private Receiver receiver;

    /**
     * Constructor pass recipient
     *
     * @param receiver
     */
    public ConcreteCommand1(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        //Business logic
        this.receiver.doSomething();
    }
}

public class ConcreteCommand2 extends Command {
    /**
     * Which Receiver class to command
     */
    private Receiver receiver;

    /**
     * Constructor pass recipient
     *
     * @param receiver
     */
    public ConcreteCommand2(Receiver receiver) {
        this.receiver = receiver;
    }

    @Override
    public void execute() {
        //Business logic
        this.receiver.doSomething();
    }
}

Two specific command classes are defined, which can be extended in practical application. In each command class, the constructor defines which receiver the command is issued for, and defines the main body of a command. The caller is very simple, only passing commands.

public class Invoker {
    private Command command;

    /**
     * Receiver, receiving command
     *
     * @param command
     */
    public void setCommand(Command command) {
        this.command = command;
    }

    public void action() {
        this.command.execute();
    }
}

The caller is like a receiver. No matter what the command is, it must be received and executed! Let's see how high-level modules call the command mode.

public class Client {
    public static void main(String[] args) {
        //First declare the caller Invoker
        Invoker invoker = new Invoker();
        //Define recipients
        Receiver receiver = new ConcreteReceiver1();
        //Define a command to send to the receiver
        Command command = new ConcreteCommand1(receiver);
        //Give the order to the caller for execution
        invoker.setCommand(command);
        invoker.action();
    }
}

A complete command mode is completed and readers can extend it.

2. It's hard to be a project manager

I'm the project manager of the company. I work on projects in China. The project manager needs to know everything and manage everything. Well done, the project manager can get a piece of the cake; if not well done, it is the responsibility of the project manager. It's almost absolute. I've taken many projects. As soon as the administrative order is down, there is one way: finish it!

Although our company is a group company, the performance of our department is calculated independently, that is to say, our department can not only serve the group company, but also other Party A, and earn more extra fast. In 2007, I was in charge of a relatively small project (but the contract amount of the project is quite large) - to establish an internal management system for a travel agency. The internal management system is used to manage customers, tourism resources, ticketing and internal affairs, which is similar to a small MIS system as a whole. The needs of customers are quite clear, because they have purchased a set of internal management system by themselves, and this change is basically a reprint; moreover, this travel agency also has its own IT department, and the technical personnel are interlinked with each other in language, easy to get along with each other, and there is no communication gap.

The members of the project are divided into requirements group (RG), art group (PG), Code Group (we also have a more elegant name: logic implementation group, which uses the name you often call, namely Code Group (CG for short), plus ten project managers like me. At the beginning, customers (i.e. travel agencies, Party A) would like to discuss with each of our groups, such as discussing requirements with demand groups, discussing pages with artists, discussing implementation with code groups, telling them to modify, delete and add various contents. This is a common mode of cooperation between Party A and Party B. when Party A goes deep into Party B's project development, we can use class diagram to represent this process, as shown in the figure.

The class diagram is very simple, and it's reasonable that customers and three groups have communication. Let's look at the implementation of this process. First, let's look at the abstract class Group.

public abstract class Group {
    /**
     * Party A and Party B work separately. If you want to discuss with a group, you need to find the group first
     */
    public abstract void find();

    /**
     * Required to add function
     */
    public abstract void add();

    /**
     * Required to delete function
     */
    public abstract void delete();

    /**
     * Required to modify function
     */
    public abstract void change();

    /**
     * All change plans are required
     */
    public abstract void plan();
}

Look at each method in the abstract class, each of which is a command tone - "find it, add, delete, plan for me!" These are commands, which are given and then executed by the relevant personnel. Let's look at three implementation classes, of which the requirement group is the most important, the requirement group class.

public class RequirementGroup extends Group {
    @Override
    public void find() {
        System.out.println("Find requirements group...");
    }

    @Override
    public void add() {
        System.out.println("Customer requests for an additional requirement...");
    }

    @Override
    public void delete() {
        System.out.println("Customer request to delete a requirement...");
    }

    @Override
    public void change() {
        System.out.println("Customer requests to modify a requirement...");
    }

    @Override
    public void plan() {
        System.out.println("Customer requirements change plan...");
    }
}

The demand group has it. Let's see the art group again. The art team is also very important. It's the face of the project, and what the customer finally touches is the interface. Art group PageGroup class.

public class PageGroup extends Group {
    @Override
    public void find() {
        System.out.println("Find art team...");
    }

    @Override
    public void add() {
        System.out.println("Customer requests for an additional requirement...");
    }

    @Override
    public void delete() {
        System.out.println("Customer request to delete a requirement...");
    }

    @Override
    public void change() {
        System.out.println("Customer requests to modify a requirement...");
    }

    @Override
    public void plan() {
        System.out.println("Customer requirements change plan...");
    }
}

Finally, look at the code group. The members of this group are usually dull, don't talk much, but do more things, which is a typical feature of this group. Code group CodeGroup class.

public class CodeGroup extends Group {
    @Override
    public void find() {
        System.out.println("Code group found...");
    }

    @Override
    public void add() {
        System.out.println("Customer requests for an additional requirement...");
    }

    @Override
    public void delete() {
        System.out.println("Customer request to delete a requirement...");
    }

    @Override
    public void change() {
        System.out.println("Customer requests to modify a requirement...");
    }

    @Override
    public void plan() {
        System.out.println("Customer requirements change plan...");
    }
}

The three pillars of the whole project have been created. It depends on how the customer talks with us. At the beginning, the customer submitted a relatively complete demand written by themselves. The demand group wrote an analysis specification based on this demand. After the customer looked at it, he requested to increase the demand. The scenario is as follows:

public class Client {
    public static void main(String[] args) {
        //First, the customer finds the requirement group and says, come and talk about the requirement and modify it
        System.out.println("----------Customer requests for an additional requirement----------");
        Group rg = new RequirementGroup();
        //Find requirements group
        rg.find();
        //Add a demand
        rg.add();
        //Change plan required
        rg.plan();
    }
}

The results of the operation are as follows:

----------Customer requests for an additional requirement----------
Find requirements group
The customer asked for an additional requirement
Customer request change plan

The customer's needs were met for the time being. After a while, the customer asked "draw one more interface, come and talk", so there was another scene change.

public class Client {
    public static void main(String[] args) {
        //First, the customer finds the demand group and says, "come here to talk about the page, and modify it."
        System.out.println("----------Customer requests to delete a page----------");
        Group pg = new PageGroup();
        //Page group found
        pg.find();
        //Delete a page
        pg.delete();
        //Change plan required
        pg.plan();
    }
}

The operation results are as follows:

----------Customer requests to delete a page----------
Find art team
Customer request to delete a requirement
Customer request change plan

OK, I've talked about the interface. It shouldn't be a big problem. After a day, the customer let the code group go, saying that it was a database design problem, and then called the art group to go, and arranged a bunch of commands This is not written one by one, we should be able to understand! The problem is coming, we can modify it, but each time we call a group, assign a task, and then make a plan. This is always the case. If you are Party A, are you bored? And it's easy to make mistakes in this way, and it really happened. The customer called the artist and wanted to delete it, but the artist said that the demand was written in this way, and then the customer ordered the demand group to go there again and again. After tossing and turning over again and again, the customer was also upset, so he directly grasped me as the project manager and said, "no matter what your internal arrangement is, you will find me a person in charge of the connection, and I will tell him how to do it, delete the page, add functions, and within you I don't care how the Department deals with it. I'll tell him what I want to do... "

I heard, OK, that's exactly what I want. Our brothers in the project team can't stand it anymore, so I changed my way of handling it, as shown in the figure.

An Invoker class is added to the original class diagram. Its function is to arrange different team members to work according to the customer's commands. For example, the customer said "delete a record on the interface". After receiving the String type command, the Invoker class informs the art group PageGroup to start deleting, finds the code group CodeGroup and does not save it in the background of the database, and feeds back to the customer Execution plans. This is a good solution, but the customer's command is of String type, which has many changes. It is not a very good solution to pass the command only through a String, because in the system design, the String is not binding, and judging the relevant business logic according to the String is not an excellent solution. Then how can it be a good plan? The solution is to encapsulate the commands issued by customers. Each command is an object to avoid communication errors among customers, principals and team members. The result after encapsulation is that as long as the customer says a command, my project team will start immediately without thinking about and analyzing the command String, as shown in the figure.

The Command abstract class has only one method, execute. Its function is to execute the Command. The subclass implements the Command very firmly. Similar to the army, the superior officer issues the Command to the soldier: climb the flagpole! Then the soldier replied: Yes,Sir! The perfect project is similar to this. The customer sends a Command to delete the page. The person in charge of the connector, Invoker, receives the Command and immediately executes the execute method of DeletePageCommand. The added classes in the class diagram are described as follows.

Command abstract class: the command sent to us by the customer defines the member variables of three working groups for use by subclasses; defines an abstract method, execute, which is implemented by subclasses.

CInvoker implementation class: Project connector leader, setComand receives the command from the customer. The action method is to execute the customer's command (the method name is written as action, which is different from the command's execute to avoid confusion). Among them, the command abstract class is the core of the whole extension.

public abstract class Command {
    /**
     * Define all three groups. Subclasses can be used directly
     */
    protected RequirementGroup rg = new RequirementGroup();
    protected PageGroup pg = new PageGroup();
    protected CodeGroup cg = new CodeGroup();

    /**
     * There's only one way. What do you want me to do
     */
    public abstract void execute();
}

Abstract class is very simple, the concrete implementation class only needs to implement the execute method. In a project, requirement increase is very common. Define "increase requirement" as a command AddRequirementCommand class.

public class AddRequirementCommand extends Command {
    @Override
    public void execute() {
        //Find requirements group
        super.rg.find();
        //Add a demand
        super.rg.add();
        //Give a plan
        super.rg.plan();
    }
}

Page changes also occur frequently. Define a command DeletePageCommand class to delete pages.

public class DeletePageCommand extends Command {
    @Override
    public void execute() {
        //Page group found
        super.pg.find();
        //Delete a page
        super.pg.delete();
        //Give a plan
        super.pg.plan();
    }
}

Command abstract classes can have N subclasses, such as adding a function command, deleting a requirement command, etc., which will not be described here. As long as the behavior generated by customers and frequently can be defined as a command, its implementation classes are relatively simple, and readers can expand it by themselves. The order sent by the customer has been determined. Let's look at the person in charge, Invoker.

public class Invoker {
    /**
     * What order
     */
    private Command command;

    /**
     * Receive orders from customers
     *
     * @param command
     */
    public void setCommand(Command command) {
        this.command = command;
    }

    /**
     * Execute customer's orders
     */
    public void action() {
        this.command.execute();
    }
}

It's simpler. The person in charge will execute the order as soon as he receives the order from the customer. We simulate the process of adding a requirement.

public class Client {
    public static void main(String[] args) {
        //Define our contacts
        Invoker xiaoSan = new Invoker();
        //Customer requests for an additional requirement
        System.out.println("----------Customer requests for an additional requirement----------");
        //The client gave us an order
        Command command = new AddRequirementCommand();
        //The connector receives the command
        xiaoSan.setCommand(command);
        //Contact person executes command
        xiaoSan.action();
    }
}

The operation results are as follows:

----------Customer requests for an additional requirement----------
Find requirements group
The customer asked for an additional requirement
Customer request change plan

Is our scene class much simpler? The client just gives the order and I'll execute it right away. Simple! It's simple! Let's see, then, how big our changes are if the customer asks to delete a page.

public class Client {
    public static void main(String[] args) {
        //Define our contacts
        Invoker xiaoSan = new Invoker();
        //Customer requests for an additional requirement
        System.out.println("----------Customer requests to delete a page----------");
        //The client gave us an order
        Command command = new DeletePageCommand();
        //The connector receives the command
        xiaoSan.setCommand(command);
        //Contact person executes command
        xiaoSan.action();
    }
}

The operation results are as follows:

----------Customer requests to delete a page----------
Find art team
Customer request to delete a requirement
Customer request change plan

It's not easy to modify so much, and the customer doesn't need to know who will modify it. The requirement of high cohesion is reflected. This is the command mode.

3. Advantages and disadvantages of command mode

Advantages of command mode

● decoupling between classes

There is no dependency between the caller's role and the receiver's role. When the caller realizes the function, he only needs to call the execute method of the Command abstract class. He does not need to know which receiver is executing.

● scalability

The subclass of Command can be easily extended, while the caller Invoker and the high-level module Client do not produce serious code coupling.

● command mode combined with other modes will be better

Command mode can be combined with responsibility chain mode to achieve command family analysis task; combined with template method mode, the expansion of command subclass can be reduced.

Disadvantages of command mode

The Command mode also has disadvantages. Please see the subclass of Command: if there are N commands, the problem will come out. The subclass of Command is not several, but N, which expands greatly. This requires the reader to carefully consider using it in the project.

 

 

Tags: Database

Posted on Sun, 07 Jun 2020 22:58:24 -0400 by nathanmaxsonadil