我回来啦!今天是周六,一看命令模式还有一个总结未完成,赶紧爬起来做做好。
就如上一篇所说的,如果拥有了一个遥控器,却无法光凭按下一个按你,就同时能弄暗灯光、打开音响和电视、设置到DVD,并让热水器开始加温,那么我要这个遥控器还有什么意义呢?
使用宏命令
根据比较高级的想法来看,就是我们需要制造一种新的命令,用来执行其他一堆命令,而不只是执行一个命令,这样就是一个不错的想法了吧。这就是我们将要说的宏命令。
public class MacroCommand implements Command { // 在宏命令中,用命令数组存储一大堆命令 Command[] commands; public MacroCommand(Command[] commands) { this.commands = commands; } public void execute() { for (int i = 0; i < commands.length; i++) { // 当这个宏命令被遥控器执行时,就一次性执行数组里的每个命令 commands[i].execute(); } } /** * NOTE: these commands have to be done backwards to ensure * proper undo functionality */ public void undo() { for (int i = commands.length -1; i >= 0; i--) { commands[i].undo(); } }}class MacroCommand implements Command {
// 在宏命令中,用命令数组存储一大堆命令
Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
public void execute() {
for (int i = 0; i < commands.length; i++) {
// 当这个宏命令被遥控器执行时,就一次性执行数组里的每个命令
commands[i].execute();
}
}
/**
* NOTE: these commands have to be done backwards to ensure
* proper undo functionality
*/
public void undo() {
for (int i = commands.length -1; i >= 0; i--) {
commands[i].undo();
}
}
}
让我们来看下如何使用宏命令:
-
先创建想要进入宏的命令集合
Light light = new Light("Living Room");TV tv = new TV("Living Room");Stereo stereo = new Stereo("Living Room");Hottub hottub = new Hottub();LightOnCommand lightOn = new LightOnCommand(light);StereoOnCommand stereoOn = new StereoOnCommand(stereo);TVOnCommand tvOn = new TVOnCommand(tv);HottubOnCommand hottubOn = new HottubOnCommand(hottub);"Living Room");
TV tv = new TV("Living Room");
Stereo stereo = new Stereo("Living Room");
Hottub hottub = new Hottub();
LightOnCommand lightOn = new LightOnCommand(light);
StereoOnCommand stereoOn = new StereoOnCommand(stereo);
TVOnCommand tvOn = new TVOnCommand(tv);
HottubOnCommand hottubOn = new HottubOnCommand(hottub);
-
接下来创建两个数组,其中一个用来记录开启命令,另一个用来记录关闭命令,并在数组内放入对应的命令
Command[] partyOn = { lightOn, stereoOn, tvOn, hottubOn};Command[] partyOff = { lightOff, stereoOff, tvOff, hottubOff};MacroCommand partyOnMacro = new MacroCommand(partyOn);MacroCommand partyOffMacro = new MacroCommand(partyOff);
MacroCommand partyOnMacro = new MacroCommand(partyOn);
MacroCommand partyOffMacro = new MacroCommand(partyOff);
-
然后将宏命令指定给我们所希望的按钮:
remoteControl.setCommand(0, partyOnMacro, partyOffMacro);
-
最后,只需按下一些按钮,测试是否正常工作
System.out.println(remoteControl);System.out.println("--- Pushing Macro On---");remoteControl.onButtonWasPushed(0);System.out.println("--- Pushing Macro Off---");remoteControl.offButtonWasPushed(0);"--- Pushing Macro On---");
remoteControl.onButtonWasPushed(0);
System.out.println("--- Pushing Macro Off---");
remoteControl.offButtonWasPushed(0);
不会忘记我们的撤销功能
public void undo() { for (int i = commands.length -1; i >= 0; i--) { commands[i].undo(); }}
for (int i = commands.length -1; i >= 0; i--) {
commands[i].undo();
}
}
命令模式的更多用途:队列请求
命令可以将运算块打包(一个接收者和一组动作),然后将它传来传去,就像是一般的对象一样。现在,即使在命令被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的特性衍生一些应用,例如:日程安排、线程池、工作队列等。
想象一个工作队列:你再某一端添加命令,然后另一端则是线程。线程进行下面的动作:从队列中取出一个命令,调用它的execute()方法,等待这个调用完成,然后将此命令对象丢弃,再取出下一个命令。。。
请注意,工作队列类和进行计算的对象之间完全是解耦的。此刻线程可能在进行财务运算,下一刻却在读取网络数据。工作队列对象不在乎到底做些什么,他们只知道取出命令对象,然后调用其execute()方法。
命令模式的更多用途:日志请求
某些应用需要我们将所有的动作都记录在日志中,并能在系统死机之后,重新调用这些动作恢复到之前的状态。命令模式能够支持这一点。
当我们执行命令的时候,将历史记录存储在磁盘中,一旦系统死机,我们就可以将命令对象重新加载,并成批地依次调用这些对象的execute()方法。
比如有许多调用大型数据结构的应用无法在每次改变发生时被快速地存储。通过使用记录日志,我们可以将上次检查点之后的所有操作记录下来,如果系统出状况,从检查点开始应用这些操作。比如说,对于电子表格应用,我们可能想要实现的错误回复方式是将电子表格的操作记录在日志中,而不是每次电子表格一有变化就记录整个电子表格。
对于更高级的应用而言,这些技巧可以被扩展应用到事务处理中,也就是说,一整群操作必须全部进行完成,或者没有进行任何的操作。
设计箱内的工具
还是按照之前的套路,总结下工具箱内新增的工具吧
我们学习命令模式,就是一个循序渐进的过程,先从简单的开关开始,然后再到复杂的开启状态,进而想到队列,想到记录日志这种事务操作。学习也是一个循序渐进的过程,我们通过简单的Demo学习,然后到项目实践,再到后面独立设计框架,我相信大家都能悟出这个道理来。
持续学习,精进学习,我们会一起加油。下一次,我们将学习适配器模式与外观模式
爱生活,爱学习,爱感悟,爱挨踢