Java 设计模式中的命令模式详情
作者:爱学习的大鱼
这篇文章主要介绍了Java 设计模式中的命令模式详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
模式介绍
- 命令模式(Command Pattern) :在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计。
- 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
- 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
UML类图
类图解析:
- Invoker:是调用者角色。
- Command:是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
- ConcreteCommand:将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute、undo方法
- Receiver:接受者角色,知道如何实施和执行一个请求相关的操作
命令模式案例
案例解析:智能家居,通过一个遥控器控制家里的智能设备
Command接口类
public interface Command { /** * 执行操作 */ void execute(); /** * 撤销操作 */ void undo(); }
LightReceiver、CurtainReceiver信号接收者
public class LightReceiver { public void on() { System.out.println("开灯..."); } public void off() { System.out.println("关灯...."); } }
public class CurtainReceiver { public void on() { System.out.println("打开窗帘..."); } public void off() { System.out.println("关闭窗帘..."); } }
Command具体实现子类CurtainOnCommand、CurtainOffCommand、LightOnCommand、LightOffCommand、NoCommand
public class CurtainOnCommand implements Command { private CurtainReceiver curtain; public CurtainOnCommand(CurtainReceiver curtainReceiver) { this.curtain = curtainReceiver; } @Override public void execute() { curtain.on(); } @Override public void undo() { curtain.off(); } }
public class CurtainOffCommand implements Command { private CurtainReceiver curtainReceiver; public CurtainOffCommand(CurtainReceiver curtainReceiver) { this.curtainReceiver = curtainReceiver; } @Override public void execute() { curtainReceiver.off(); } @Override public void undo() { curtainReceiver.on(); } }
public class LightOnCommand implements Command { // 聚合LightReceiver private LightReceiver light; public LightOnCommand(LightReceiver light) { this.light = light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); } }
public class LightOffCommand implements Command { // 聚合LightReceiver private LightReceiver light; public LightOffCommand(LightReceiver light) { this.light = light; } @Override public void execute() { light.off(); } @Override public void undo() { light.on(); } }
/** * 用于初始化每个按钮 */ public class NoCommand implements Command { @Override public void execute() { System.out.println("do nothing..."); } @Override public void undo() { System.out.println("do nothing..."); } }
RemoteController调用者
public class RemoteController { // 开命令数组 private Command[] onCommands; // 关命令数组 private Command[] offCommands; // 撤销命令位置 private int no; // 撤销命令是否为按下命令 private Boolean isOn; /** * 初始化 */ public RemoteController() { this.onCommands = new Command[5]; this.offCommands = new Command[5]; for (int i = 0; i < 5; i++) { onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand(); } } /** * 设置一行中的按钮 * @param no * @param onCommand * @param offCommand */ public void setCommand(int no,Command onCommand,Command offCommand) { onCommands[no] = onCommand; offCommands[no] = offCommand; } /** * 开按钮按下 * @param no */ public void onButtonPushed(int no){ // 调用按钮方法 onCommands[no].execute(); // 记录撤销按钮 this.no = no; isOn = true; } /** * 关按钮按下 * @param no */ public void offButtonPushed(int no) { // 调用按钮方法 offCommands[no].execute(); // 记录撤销按钮 this.no = no; isOn = false; } public void undo() { if (isOn) { onCommands[no].undo(); } else { offCommands[no].undo(); } isOn = !isOn; } }
Client测试类
public class Client { public static void main(String[] args) { // 接收者 LightReceiver lightReceiver = new LightReceiver(); CurtainReceiver curtainReceiver = new CurtainReceiver(); // 命令 Command lightOnCommand = new LightOnCommand(lightReceiver); Command lightOffCommand = new LightOffCommand(lightReceiver); Command curtainOnCommand = new CurtainOnCommand(curtainReceiver); Command curtainOffCommand = new CurtainOffCommand(curtainReceiver); // 执行者 RemoteController remoteController = new RemoteController(); remoteController.setCommand(0,lightOnCommand,lightOffCommand); remoteController.setCommand(1,curtainOnCommand,curtainOffCommand); System.out.print("开灯按钮\t"); remoteController.onButtonPushed(0); System.out.print("撤销按钮\t"); remoteController.undo(); System.out.print("撤销按钮\t"); remoteController.undo(); System.out.print("关灯按钮\t"); remoteController.offButtonPushed(0); System.out.println("------------------"); System.out.print("开窗帘按钮\t"); remoteController.onButtonPushed(1); System.out.print("撤销按钮 \t"); remoteController.undo(); System.out.print("撤销按钮 \t"); remoteController.undo(); System.out.print("关窗帘按钮\t"); remoteController.offButtonPushed(1); } }
测试结果:
个人优化方案(仅供参考):
命令模式的注意事项和细节
- 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute(方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
- 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令。
- 容易实现对请求的撤销和重做。
- 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意。
- 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
- 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD (DOS命令)订单的撤销/恢复、触发-反馈机制。