Design Mode-Command Mode

1 Introduction

In a software development system, there is often a close coupling relationship between the "method requester" and the "method implementer", which is not conducive to the expansion and maintenance of software functions. For example, it is inconvenient to "undo, redo, record" a method, so "how to decouple the requester and implementer of the method?" becomes very important, and the command mode can solve this well. problem.

In real life, there are many examples of command patterns. For example, when watching TV, we only need to press the remote control to complete the channel switching. This is the command mode, which completely decouples the channel switching request and channel switching processing. The TV remote control (command sender) uses buttons (specific commands) to remotely control the TV (command receiver).

For another example, when we go to a restaurant to eat, the menu is not customized until the guests arrive, but has been pre-configured. In this way, guests only need to order dishes when they come, instead of allowing guests to make temporary orders. The menu provided by the restaurant is equivalent to decoupling the request and processing, which is the embodiment of the command mode.

2. Definition and characteristics of command mode

The command mode is defined as follows: encapsulate a request as an object, and separate the responsibility of issuing the request from the responsibility of executing the request. In this way, the two communicate through the command object, so that it is convenient to store, transfer, call, add and manage the command object.

The main advantages of the command mode are as follows:

  1. Reduce the coupling degree of the system by introducing middleware (abstract interface).
  2. Extensibility is good, and it is very convenient to add or delete commands. Using command mode to add and delete commands will not affect other categories, and meet the "opening and closing principle".
  3. Macro commands can be realized. The command mode can be combined with the combined mode to assemble multiple commands into a combined command, that is, a macro command.
  4. Convenient to realize Undo and Redo operations. The command mode can be combined with the memo mode described later to realize the cancellation and restoration of commands.
  5. Additional functions can be added on the basis of existing commands. For example, logging, combined with the decorator mode will be more flexible.

The disadvantages are:

  1. A large number of specific command classes may be generated. Because each specific operation needs to design a specific command class, this will increase the complexity of the system.
  2. The result of the command mode is actually the execution result of the receiver, but in order to structure, decouple the request and implementation in the form of a command, an additional type structure is introduced (the requester and the abstract command interface are introduced), which increases the difficulty of understanding. But this is also a common problem of design patterns. Abstraction will inevitably increase the number of classes. Code extraction is definitely more difficult to understand than code aggregation.

3. The structure and realization of command mode

The related operations in the system can be abstracted into commands to separate the caller from the implementer. The structure is as follows:

1. The structure of the model

The command mode includes the following main roles:

  1. The role of the abstract command class (Command): declares the interface for executing the command, and has the abstract method execute() for executing the command.
  2. The role of the concrete command class (Concrete Command): it is the concrete realization class of the abstract command class. It owns the receiver object and completes the operation of the command by calling the receiver's function.
  3. Implementer/Receiver (Receiver) role: perform the related operations of the command function and be the real implementer of the specific command object business.
  4. Invoker/requester (Invoker) role: it is the sender of the request, it usually has a lot of command objects, and executes related requests by accessing the command object, it does not directly access the receiver

Insert picture description here

2. Implementation of the pattern

package command;

public class CommandPattern {
    
    
    public static void main(String[] args) {
    
    
        Command cmd = new ConcreteCommand();
        Invoker ir = new Invoker(cmd);
        System.out.println("客户访问调用者的call()方法...");
        ir.call();
    }
}

//调用者
class Invoker {
    
    
    private Command command;

    public Invoker(Command command) {
    
    
        this.command = command;
    }

    public void setCommand(Command command) {
    
    
        this.command = command;
    }

    public void call() {
    
    
        System.out.println("调用者执行命令command...");
        command.execute();
    }
}

//抽象命令
interface Command {
    
    
    public abstract void execute();
}

//具体命令
class ConcreteCommand implements Command {
    
    
    private Receiver receiver;

    ConcreteCommand() {
    
    
        receiver = new Receiver();
    }

    public void execute() {
    
    
        receiver.action();
    }
}

//接收者
class Receiver {
    
    
    public void action() {
    
    
        System.out.println("接收者的action()方法被调用...");
    }
}

The customer visits the caller's call() method... The
caller executes the command command... The
receiver's action() method is called...

4. Application examples of command mode

[Example 1] Using the command mode to achieve an example of a customer going to a restaurant for breakfast

Analysis: Customers can choose rice noodles, pho and wontons for breakfast when they go to restaurants. Customers can choose several of the above breakfasts from the waiter, and the waiter will give the customer's request to the relevant chef to make it. Here, ordering breakfast is equivalent to "command", waiter is equivalent to "caller", and chef is equivalent to "receiver", so it is more appropriate to use command mode.

  • First of all, define a breakfast class (Breakfast), which is an abstract command class, with an abstract method cooking(), indicating what to do;
  • Then define its subclasses ChangFen, HunTun, and HeFen. They are specific command classes that implement the cooking() method of the breakfast class, but they will not be specific, but will be handed over. Do it for specific chefs;
  • Specific chefs include ChangFenChef, HunTunChef, and HeFenChef. They are the recipients of the order.

Since this example wants to show the effect of the chef cooking, we define each chef class as a subclass of JFrame; finally, define the waiter class (Waiter), which receives the cooking request from the customer and sends out the cooking command. The customer class orders dishes through the waiter class. Figure 2 shows its structure.

Insert picture description here

package command;

import javax.swing.*;

public class CookingCommand {
    
    
    public static void main(String[] args) {
    
    
        Breakfast food1 = new ChangFen();
        Breakfast food2 = new HunTun();
        Breakfast food3 = new HeFen();
        Waiter fwy = new Waiter();
        fwy.setChangFen(food1);//设置肠粉菜单
        fwy.setHunTun(food2);  //设置河粉菜单
        fwy.setHeFen(food3);   //设置馄饨菜单
        fwy.chooseChangFen();  //选择肠粉
        fwy.chooseHeFen();     //选择河粉
        fwy.chooseHunTun();    //选择馄饨
    }
}

//调用者:服务员
class Waiter {
    
    
    private Breakfast changFen, hunTun, heFen;

    public void setChangFen(Breakfast f) {
    
    
        changFen = f;
    }

    public void setHunTun(Breakfast f) {
    
    
        hunTun = f;
    }

    public void setHeFen(Breakfast f) {
    
    
        heFen = f;
    }

    public void chooseChangFen() {
    
    
        changFen.cooking();
    }

    public void chooseHunTun() {
    
    
        hunTun.cooking();
    }

    public void chooseHeFen() {
    
    
        heFen.cooking();
    }
}

//抽象命令:早餐
interface Breakfast {
    
    
    public abstract void cooking();
}

//具体命令:肠粉
class ChangFen implements Breakfast {
    
    
    private ChangFenChef receiver;

    ChangFen() {
    
    
        receiver = new ChangFenChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//具体命令:馄饨
class HunTun implements Breakfast {
    
    
    private HunTunChef receiver;

    HunTun() {
    
    
        receiver = new HunTunChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//具体命令:河粉
class HeFen implements Breakfast {
    
    
    private HeFenChef receiver;

    HeFen() {
    
    
        receiver = new HeFenChef();
    }

    public void cooking() {
    
    
        receiver.cooking();
    }
}

//接收者:肠粉厨师
class ChangFenChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    ChangFenChef() {
    
    
        super("煮肠粉");
        l.setIcon(new ImageIcon("src/command/ChangFen.jpg"));
        this.add(l);
        this.setLocation(30, 30);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

//接收者:馄饨厨师
class HunTunChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    HunTunChef() {
    
    
        super("煮馄饨");
        l.setIcon(new ImageIcon("src/command/HunTun.jpg"));
        this.add(l);
        this.setLocation(350, 50);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

//接收者:河粉厨师
class HeFenChef extends JFrame {
    
    
    private static final long serialVersionUID = 1L;
    JLabel l = new JLabel();

    HeFenChef() {
    
    
        super("煮河粉");
        l.setIcon(new ImageIcon("src/command/HeFen.jpg"));
        this.add(l);
        this.setLocation(200, 280);
        this.pack();
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void cooking() {
    
    
        this.setVisible(true);
    }
}

5. Application scenarios of command mode

When a certain operation of the system has command semantics and the command realization is unstable (changes), the command mode can be used to decouple the request and realization. The abstract command interface is used to stabilize the requester's code structure and encapsulate the implementation details of the receiver's specific commands. The receiver and the abstract command are weakly coupled (the internal methods do not need to be consistent) and have good scalability.

Command mode is usually suitable for the following scenarios

  1. When the request caller needs to be decoupled from the request receiver, the command mode can prevent the caller and receiver from interacting directly.
  2. When the system randomly requests commands or frequently adds or deletes commands, the command mode can easily realize these functions.
  3. When the system needs to perform a set of operations, the command mode can define macro commands to achieve this function.
  4. When the system needs to support the undo (Undo) operation and redo operation of the command, the command object can be stored and implemented in the memo mode

6. Expansion of command mode

In software development, sometimes the command mode is used in conjunction with the previously learned combination mode, which constitutes the macro command mode, also called the combined command mode. The macro command contains a group of commands, which act as the dual role of the specific command and the caller. When it is executed, all the commands it contains will be called recursively. The specific structure diagram is shown in Figure 3.

Insert picture description here

package command;

import java.util.ArrayList;

public class CompositeCommandPattern {
    
    
    public static void main(String[] args) {
    
    
        AbstractCommand cmd1 = new ConcreteCommand1();
        AbstractCommand cmd2 = new ConcreteCommand2();
        CompositeInvoker ir = new CompositeInvoker();
        ir.add(cmd1);
        ir.add(cmd2);
        System.out.println("客户访问调用者的execute()方法...");
        ir.execute();
    }
}

//抽象命令
interface AbstractCommand {
    
    
    public abstract void execute();
}

//树叶构件: 具体命令1
class ConcreteCommand1 implements AbstractCommand {
    
    
    private CompositeReceiver receiver;

    ConcreteCommand1() {
    
    
        receiver = new CompositeReceiver();
    }

    public void execute() {
    
    
        receiver.action1();
    }
}

//树叶构件: 具体命令2
class ConcreteCommand2 implements AbstractCommand {
    
    
    private CompositeReceiver receiver;

    ConcreteCommand2() {
    
    
        receiver = new CompositeReceiver();
    }

    public void execute() {
    
    
        receiver.action2();
    }
}

//树枝构件: 调用者
class CompositeInvoker implements AbstractCommand {
    
    
    private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>();

    public void add(AbstractCommand c) {
    
    
        children.add(c);
    }

    public void remove(AbstractCommand c) {
    
    
        children.remove(c);
    }

    public AbstractCommand getChild(int i) {
    
    
        return children.get(i);
    }

    public void execute() {
    
    
        for (Object obj : children) {
    
    
            ((AbstractCommand) obj).execute();
        }
    }
}

//接收者
class CompositeReceiver {
    
    
    public void action1() {
    
    
        System.out.println("接收者的action1()方法被调用...");
    }

    public void action2() {
    
    
        System.out.println("接收者的action2()方法被调用...");
    }
}

The client visits the caller’s execute() method... the
receiver’s action1() method is called... the
receiver’s action2() method is called...

Guess you like

Origin blog.csdn.net/saienenen/article/details/112674669