照例先搬上来 Head First Design Patterns 中的定义,命令模式将请求封装为对象,从而允许使用不同的请求,队列或日志请求参数化其他对象,并支持可撤销操作。
是不是很抽象? 放个类图来理解一下它的组成
- Client(客户):负责创建一个具体的命令(Concrete Command)
- Invoker(调用者):调用者持有一个命令对象,并在某个时刻调用命令对象的 execute() 方法。
- Command(命令接口):包含命令对象的 execute() 方法和 undo() 方法。
- ConcreteCommand(具体命令):实现命令接口。包括两个操作,执行命令和撤销命令。
- Receiver(接收者):接受命令并执行。
课本里举了在餐厅的例子,顾客到了餐厅,在服务员的帮助下点完餐,然后由服务员将点餐的内容告知给厨师,厨师再根据菜单做出相应的美味佳肴。在这个例子里,顾客就是 client,服务员就是 invoker,顾客点的菜就是 concrete command, 厨师就是 receiver,invoker 像是客户和对应命令执行者之间的中间,客户会告诉它需要干什么,它接到这个请求之后会告知给相应的命令对象,最后命令对象接到通知后就会执行命令。
代码
基本功能实现
//命令接口
public interface Command {
public void execute ();
}
//第一个命令对象
public class LightOnCommand implements Command {
Light light;
public LightOnCommand (Light light) {
this.light = light;
}
public void execute () {
light.on();
}
}
//使用命令对象
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl () { }
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
//测试该遥控功能
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light(); //被控制的对象,命令的作用者
LightOnCommand lightOn = new LightOnCommand(light); //具体的命令
remote.setCommand(lightOn);
remote.buttonWasPressed();
}
}
添加了一系列指令的遥控器,这里先介绍一下null object,很tricking的一种实现
//一系列的指令
public class RemoteControl{
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl(){
onCommands = new Command[7];
offCommands = new Command[7];
...;//初始化onCommands offCommands和undoCommand都为noCommand
}
public void setCommand(int slot, Command onCommand, Command offCommand){
onCommands[slot] = onCommand;
...;
}
public void onButtonWasPushed (int slot){
onCommands[slot].execute();
undoCommand = onCommand[slot];
}
...;
}
//添加undo功能
public interface Command {
public void execute ();
public void undo ();
}
课本最后还介绍了宏命令(macro command)就是一系列命令的组合,只需按一个按钮,实现命令的批处理,这个功能实现起来比较简单也就不再多说了。
下面的部分个人理解的不是很多,摘录于引用三:
应用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队(如:线程池+工作队列)和执行请求。
3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4.系统需要将一组操作组合在一起,即支持宏命令。
适用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队(如:线程池+工作队列)和执行请求。 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
3.系统需要将一组操作组合在一起,即支持宏命令。
优缺点:
优点:
1.降低系统的耦合度:Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
2.Command是头等的对象。它们可像其他的对象一样被操纵和扩展。
3.组合命令:你可将多个命令装配成一个组合命令,即可以比较容易地设计一个命令队列和宏命令。一般说来,组合命令是Composite模式的一个实例。
4.增加新的Command很容易,因为这无需改变已有的类。
5.可以方便地实现对请求的Undo和Redo。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
更多用途:
1.宏命令模式:命令模式 加 组合模式,我们可以将多个命令组合到一起来实现命令的批处理。
2.队列请求:将命令排成一个队列打包,一个个调用 execute 方法,如线程池的任务队列,线程不关心任务队列中是读 IO 还是计算,只取出命令后执行,接着进行下一个。
3.日志请求:某些应用需要我们将所有的动作记录在日志中,然后在系统死机等情况出现时,重新调用这些动作恢复到之前的状态。如数据库事务。
参考资料:
- Head First Design Patterns
- 老师上课的PPT
- https://blog.csdn.net/wwh578867817/article/details/51533263