如果设计一个遥控器对电视操作你会怎么设计
ON() /OFF() if() else if() else ???
命令模式可以将"动作的请求者"从"动作的执行者"对象中解耦
利用对象命令,把请求("打开电灯")封装成一个对象,每个按钮存储一个命令,按下按钮就可以请命令对象做相关的工作,遥控器不知道工作内容是什么.只有命令对象能和电视对象沟通
或者换一种思路
客户 ==>点餐(creatOrder())==>服务员带走菜单(takeOrder())==>通知厨师(OrderUp())==>厨师工作
客户和厨师没有耦合,一个订单封装了所有请求
现在来设计一段代码,设计一个遥控器,可以操作灯,音响,车库门等等(这次的代码可能有点长,请选择跳跃式观看)
/** * 命令接口,通用启动方法 * @author Dougest * 2017年6月29日 上午10:54:46 * */ public interface Command { public void execute();//启动 public void undo();//撤销 } /** * 风扇类 * @author Dougest * 2017年7月3日 下午1:17:00 * */ public class CelingFan { public static final int HIGH = 3; public static final int MEDIUM = 2; public static final int LOW = 1; public static final int OFF = 0; String location; int speed; public CelingFan(String location) { this.location = location; this.speed = OFF; } public void heigh() { speed = HIGH; System.out.println("当前速度 => "+speed); } public void medium() { speed = MEDIUM; System.out.println("当前速度 => "+speed); } public void low() { speed = LOW; System.out.println("当前速度 => "+speed); } public void off() { speed = OFF; System.out.println("当前速度 => "+speed); } public int getSpeed() { return speed; } } /** * 车库门类 * @author Dougest * 2017年6月29日 上午10:53:58 * */ public class GarageDoor { String position; public GarageDoor(){ position = ""; } public GarageDoor(String position){ this.position = position; } public void open(){ System.out.println(position + "门开了..............."); } public void close(){ System.out.println(position + "门关了..............."); } } /** * 灯 * @author Dougest * 2017年6月29日 上午10:54:15 * */ public class Light { String position; public Light() { position = ""; } public Light(String position) { this.position = position; } public void on() { System.out.println(position + "灯开咯..................."); } public void off() { System.out.println(position + "灯关咯..................."); } } /** * 音响类 * @author Dougest * 2017年7月3日 下午3:26:05 * */ public class Stereo { String position; public Stereo() { position = ""; } public Stereo(String position) { this.position = position; } public void on() { System.out.println(position+"开音响了...."); } public void off() { System.out.println(position+"关音响了...."); } public void setCD() { System.out.println(position+"放CD...."); } public void setDvd() { System.out.println(position+"放DVD...."); } public void setRadio() { System.out.println(position+"点播...."); } public void setVolume(int vol) { System.out.println(position+"设置声音 => " + vol); } } /** * 电扇高速命令(状态) * @author Dougest * 2017年7月3日 下午3:21:12 * */ public class CelingFanCommandHight implements Command{ CelingFan celingFan; int prevSpeed; public CelingFanCommandHight(CelingFan celingFan) { this.celingFan = celingFan; } public void execute() { prevSpeed = celingFan.getSpeed(); celingFan.heigh(); } @Override public void undo() { switch(prevSpeed) { case CelingFan.HIGH: celingFan.heigh();break; case CelingFan.MEDIUM: celingFan.medium();break; case CelingFan.LOW: celingFan.low();break; case CelingFan.OFF: celingFan.off();break; } } } /** * 电扇低俗命令(状态) * @author Dougest * 2017年7月3日 下午3:21:33 * */ public class CelingFanCommandLow implements Command{ CelingFan celingFan; int prevSpeed; public CelingFanCommandLow(CelingFan celingFan) { this.celingFan = celingFan; } public void execute() { prevSpeed = celingFan.getSpeed(); celingFan.low(); } @Override public void undo() { switch(prevSpeed) { case CelingFan.HIGH: celingFan.heigh();break; case CelingFan.MEDIUM: celingFan.medium();break; case CelingFan.LOW: celingFan.low();break; case CelingFan.OFF: celingFan.off();break; } } } /** * 电扇中速命令(状态) * @author Dougest * 2017年7月3日 下午3:21:57 * */ public class CelingFanCommandMedium implements Command{ CelingFan celingFan; int prevSpeed; public CelingFanCommandMedium(CelingFan celingFan) { this.celingFan = celingFan; } public void execute() { prevSpeed = celingFan.getSpeed(); celingFan.medium(); } @Override public void undo() { switch(prevSpeed) { case CelingFan.HIGH: celingFan.heigh();break; case CelingFan.MEDIUM: celingFan.medium();break; case CelingFan.LOW: celingFan.low();break; case CelingFan.OFF: celingFan.off();break; } } } /** * 电扇关闭命令(状态) * @author Dougest * 2017年7月3日 下午3:22:25 * */ public class CelingFanCommandOff implements Command{ CelingFan celingFan; int prevSpeed; public CelingFanCommandOff(CelingFan celingFan) { this.celingFan = celingFan; } public void execute() { prevSpeed = celingFan.getSpeed(); celingFan.off(); } @Override public void undo() { switch(prevSpeed) { case CelingFan.HIGH: celingFan.heigh();break; case CelingFan.MEDIUM: celingFan.medium();break; case CelingFan.LOW: celingFan.low();break; case CelingFan.OFF: celingFan.off();break; } } } /** * 关门命令(普通) * @author Dougest * 2017年7月3日 下午3:22:41 * */ public class GarageDoorCloseCommand implements Command{ GarageDoor garageDoor; public GarageDoorCloseCommand(GarageDoor garageDoor){ this.garageDoor = garageDoor; } public void execute() { garageDoor.close(); } @Override public void undo() { garageDoor.open();; } } /** * 开门命令(普通) * @author Dougest * 2017年6月29日 上午10:53:46 * */ public class GarageDoorOpenCommand implements Command{ GarageDoor garageDoor; public GarageDoorOpenCommand(GarageDoor garageDoor){ this.garageDoor = garageDoor; } public void execute() { garageDoor.open(); } @Override public void undo() { garageDoor.close(); } } /** * 关灯命令(普通) * @author Dougest * 2017年7月3日 上午10:10:46 * */ public class LightOffCommand implements Command{ Light light; public LightOffCommand(Light light) { this.light = light; } public void execute() { light.off(); } @Override public void undo() { light.on(); } } /** * 开灯命令(普通) * @author Dougest * 2017年6月29日 上午10:52:47 * */ public class LightOnCommand implements Command{ Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); } @Override public void undo() { light.off(); } } /** * 按组停止(宏命令) * @author Dougest * 2017年7月3日 下午3:23:40 * */ public class MacroCommandStop implements Command{ Command[]comands; Command[]prevComands; public MacroCommandStop(Command[] comands) { this.comands = comands; } public void execute() { for(Command comand : comands) { comand.execute(); } } public void undo() { for(Command comand : comands) { comand.undo();; } } } /** * 无命令 * @author Dougest * 2017年7月3日 下午3:23:59 * */ public class NoCommand implements Command{ public void execute() { System.out.println("启动 => 空命令"); } @Override public void undo() { System.out.println("撤销 => 空命令"); } } /** * 关闭音响() * @author Dougest * 2017年7月3日 下午3:24:09 * */ public class StereoOffCommand implements Command{ Stereo stereo; public StereoOffCommand(Stereo stereo) { this.stereo = stereo; } @Override public void execute() { stereo.off(); } @Override public void undo() { stereo.on(); stereo.setCD(); stereo.setVolume(11); } } /** * 音响打开(调用多个方法) * @author Dougest * 2017年7月3日 下午3:25:07 * */ public class StereoWithCDOnWithCommand implements Command{ Stereo stereo; public StereoWithCDOnWithCommand (Stereo stereo) { this.stereo = stereo; } @Override public void execute() { stereo.on(); stereo.setCD(); stereo.setVolume(11); } @Override public void undo() { stereo.off(); } } /** * 可撤回遥控控制 * @author Dougest * 2017年7月3日 下午3:20:00 * */ public class RemoteControlUndo { Command[] onCommands; Command[] offCommands; Command undoCommand;//用来记录前一个命令 public RemoteControlUndo() { onCommands = new Command[7]; offCommands = new Command[7]; Command noCommand = new NoCommand(); for(int i = 0; i < 7; i++ ) { onCommands[i] = noCommand; offCommands[i] = noCommand; } undoCommand = noCommand; } //设置命令 public void setCommand(int slot,Command onCommand, Command offCommand) { ensureExplicitCapacity(slot); onCommands[slot] = onCommand; offCommands[slot] = offCommand; } public void setnoCommand(int slot) { onCommands[slot] = new NoCommand(); offCommands[slot] = new NoCommand(); } //打开按钮 public void onButtonWasPushed(int slot) { ensureExplicitCapacity(slot); onCommands[slot].execute(); undoCommand = onCommands[slot];//记录操作 } //关闭按钮 public void offButtonWasPushed(int slot) { ensureExplicitCapacity(slot); offCommands[slot].execute(); undoCommand = offCommands[slot];//记录操作 }//撤销按钮 public void undoButtonWasPushed(int slot) { undoCommand.undo();//撤销记录的操作 } //明确容量 只是为了模仿ArrayList public void ensureExplicitCapacity(int slot) { if(slot >= onCommands.length || slot >= offCommands.length) { grew(slot+1,onCommands,offCommands);//踩过坑才知道加1 setnoCommand(slot); } } //扩容 只是为了模仿ArrayList public void grew(int minCapacity,Command[] onCommand,Command[] offCommand) { int oldCapacity = Math.max(onCommand.length,offCommand.length); int newCapacity = oldCapacity + (oldCapacity >> 1); if(newCapacity < minCapacity) newCapacity = minCapacity; offCommands = Arrays.copyOf(offCommand,newCapacity); onCommands = Arrays.copyOf(onCommand,newCapacity); System.out.println(offCommands.length+" , "+onCommands.length); } public String toString(){ String a = ""; for(int i = 0; i < onCommands.length; i++) { a += "slot => " + i +" onCommand => "+onCommands[i].getClass().getName() +" offCommands => " + offCommands[i].getClass().getName() + "\n"; } return a; } } /** * 主测试 * @author Dougest * 2017年7月3日 下午1:48:22 * */ public class MainTest { public static void main(String[] args) { RemoteWithUndoLoader remoteWithUndoLoader = new RemoteWithUndoLoader(); //普通命令 System.out.println("\n"); System.out.println("<<<<<<<<<<普通命令>>>>>>>>>"); remoteWithUndoLoader.on(3); remoteWithUndoLoader.undo(3); System.out.println("\n"); System.out.println("<<<<<<<<<<普通命令>>>>>>>>>"); //状态命令 System.out.println("\n"); System.out.println("<<<<<<<<<<状态命令>>>>>>>>>"); remoteWithUndoLoader.on(7); remoteWithUndoLoader.off(7); remoteWithUndoLoader.undo(7); System.out.println("\n"); System.out.println("<<<<<<<<<<状态命令>>>>>>>>>"); /** * 命令宏 * 比party模式"硬编码"到命令模式中更优雅,使用更灵活,耦合度也更低 */ System.out.println("\n"); System.out.println("<<<<<<<<<<命令宏>>>>>>>>>"); System.out.println("\n"); remoteWithUndoLoader.on(8); System.out.println("\n"); remoteWithUndoLoader.off(8); System.out.println("\n"); remoteWithUndoLoader.undo(8); System.out.println("\n"); System.out.println("<<<<<<<<<<命令宏>>>>>>>>>"); System.out.println("\n"); //空命令 System.out.println("<<<<<<<<<<空命令>>>>>>>>>"); System.out.println("\n"); remoteWithUndoLoader.on(18); remoteWithUndoLoader.off(18); remoteWithUndoLoader.undo(18); System.out.println("<<<<<<<<<<空命令>>>>>>>>>"); System.out.println("\n"); } }
命令模式
将"请求"封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象
命令模式也支持可撤销操作
命令模式可以将运算块打包(一个接受者和一组动作),然后将他传来传去
现在,即使命令对象被创建许久之后,运算依然可以被调用,他甚至可以在不同的线程中被调用
可以衍生的应用:
日程安排/线程池/工作队列等
工作队列:
1.一端添加命令,另外一端是线程
2.线程进行下面的动作:
3.从队列中取出来一个命令,执行execute方法,等待调用完成,将此命令丢去,再取下一个命令
工作队列类和进行计算的对象之间是完全解耦的
此刻线程在进行财务运算,下一刻进行读取网络数据
工作队列不知道调用的对象到底在做什么,他只知道取出对象,调用execute方法
类似的,只要是实现命令模式的对象,就可以放入队列,当线程可用时,调用此对象的execute方法
日志请求
场景:
某些应用需要我们将所用动作记录在日志中,并在死机时将状态恢复到之前正常时(可以新增加两个方法store(),load())
命令模式支持这一方法,可以通过对象序列化实现这些方法,但是一般默认序列化用作对象的持久化上
思路:
执行命令时候,将历史记录存储在磁盘中.一旦系统死机,将命令对象重载,成批次的调用这些对象上的execute()
这对遥控器来说没什么意义,但是许多调用大型数据结构的动作的应用无法在每次发生改变时被快速的储备
通过记录日志,我们可以将上次检查点之后的所有操作记录下来,如果出现状况,可以从检查点开始应用这些操作
对于更高级的应用,这些技巧可以被扩展到事物处理中,也就是说,这批操作要么同时完成,要么什么都没干
要点
1.命令模式将发出请求的对象和执行请求的对象解耦
2.在被解耦的两者之间是通过命令对象进行沟通的.命令对象封装了接受者和一个或者一组动作
3.调用者通过调用命令对象的execute()发出请求,这会使得接受者的动作被调用
4.调用者可以支持撤销,做法是实现一个undo()来回到execute()被执行前的状态
5.宏命令是命令中的一种简单的延伸,允许调用多个命令.宏方法也可以支持撤销
6.实际操作时,很常见使用"聪明"命令对象,也就是直接实现了请求,而不是将工作委托给接受者
7.命令也可以实现日志和事物系统