Introduction
The Command Design Pattern completely decouples request senders and receivers. There is no direct reference relationship between sender and receiver, and the object sending the request only needs to know how to send the request, not how to complete the request.
Its definition is to encapsulate the request (command) into an object, so that the client can be parameterized with different requests (injecting different request dependencies into other objects), and it can support the queuing execution, logging, and revocation of the request (command). etc. (additional control) functions.
Typical implementation
First, define an abstract command Command interface, usually only declare a method to execute the command, the code example is as follows:
public interface Command { // business processing method void execute(); }
The specific command will implement various types of requests, and it does not complete the work itself, but delegates the call to a business logic object. The code example is as follows:
public class ConcreteCommand implements Command { // maintains a reference to the requester object private final Receiver receiver; public ConcreteCommand(Receiver receiver) { this.receiver = receiver; } // Invoke the business processing method of the request receiver public void execute() { this.receiver.action(); } }
The receiver is the object that the real command is executed, and the object that the client directly operates. The code example is as follows:
public class Receiver { public void action() { // specific operation } }
Finally, what needs to be defined is the caller Invoker class, which is responsible for initializing the request. The code example is as follows:
public class Invoker { private final List<Command> commandList; public Invoker() { this.commandList = new ArrayList<>(); } public Invoker(Command command) { this(); this.commandList.add(command); } // add command public void pushCommand(Command command) { this.commandList.add(command); } // Excuting an order public void executeAll() { for (Command command : commandList) { command.execute(); } commandList.clear(); } }
For the client, it needs to know what the receiver object it needs to operate, what commands can be executed, and how to execute these commands through the caller.
The following is a code example of the client using the command mode:
public class CommandDemo { public static void main(String[] args) { // what is the receiver object of the operation Receiver receiver = new Receiver(); // What are the commands that can be executed Command command = new ConcreteCommand(receiver); // How to execute these commands by the caller Invoker invoker = new Invoker(command); invoker.executeAll(); } }
Summarize
advantage
The main advantages of command mode are as follows:
- Reduce the coupling between requester and receiver
- New commands can be easily added to the system
- It is relatively easy to design a command queue or macro command (combined command)
- Provides a design and implementation scheme for requested undo and redo operations
shortcoming
The main disadvantages of command mode are as follows:
- May result in too many specific command classes in the system
Applicable scene
The applicable scenarios of the command mode are as follows:
- The system needs to decouple the request caller from the request receiver, so that the caller and the receiver do not interact directly
- The system needs to specify, queue, and execute requests at different times
- The system needs to support the undo operation and recovery operation of the command
- The system needs to group a set of operations together to form a macro command
source code
In JDK, the Runnable interface is similar to the command interface of the command mode.
As long as a class that implements the Runnable interface is considered a thread class, it is equivalent to the role of a specific command class in the command mode. The Thread class that implements the Runnable interface can be used either as a specific command class or as a caller.
The following is a code example of the client using Runnable and Thread:
public class ThreadDemo { public static void main(String[] args) { Runnable command = new Runnable() { @Override public void run() { System.out.println("command thread execution"); } }; Thread thread = new Thread(command); // command thread execution thread.start(); } }