设计模式之命令模式

前端之家收集整理的这篇文章主要介绍了设计模式之命令模式前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

情景:

屋里有很多家用电器,你需要设计一个遥控器,来控制所有电器的使用。

如果在遥控器中添加电器类,那就使得遥控器和具体电器类过度耦合了,遥控器不应该知道电器的实现细节。

遥控器应该简单一些,我们都知道遥控器只要一些按钮,所能做的动作仅仅是按下按钮,所以不应该包含太多的控制逻辑。

所以,这里需要用命令模式,来将“动作的请求者”从“动作的执行者”对象中解耦。

设计一个命令对象,遥控器可以执行命令对象,而不关心命令具体是做什么。

在命令对象内部,有具体的电器类和命令的具体实现,以及公开给遥控器的执行方法

一个简单的实现:

首先是一个命令接口,所有的命令都要实现这个接口,然后放到遥控器上,而遥控器只要用execute()执行就好了,不需要知道命令细节。

一个具体的命令,开灯命令。

LightOnCommand </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; LightOnCommand(Light light) { </span><span style="color: #0000ff;"&gt;this</span>.light =<span style="color: #000000;"&gt; light; } @Override </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; execute() { light.on(); }

}

灯:

"灯亮了"</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; off() { System.out.println(</span>"灯灭了"<span style="color: #000000;"&gt;); }

}

遥控器:遥控器上有插槽用来持有命令。(可以考虑成遥控器上一个按钮-_-#)

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; SimpleRemoteControl() {} </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; setCommand(Command command) { slot </span>=<span style="color: #000000;"&gt; command; } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; buttonWasPressed() { slot.execute(); }

}

测试类:

= = = remote.setCommand(lightOn); remote.buttonWasPressed(); }

}

输出:  

支持可撤销操作。

类图:

命令模式类图

上面的遥控器只有一个命令,事实上有很多的电器需要控制,而且不仅需要开命令,也需要有关闭命令。

实现遥控器类:

有七个开启按钮和七个关闭按钮。

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; RemoteControl() { onCommands </span>= <span style="color: #0000ff;"&gt;new</span> Command[7<span style="color: #000000;"&gt;]; offCommands </span>= <span style="color: #0000ff;"&gt;new</span> Command[7<span style="color: #000000;"&gt;]; Command noCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; NoCommand(); </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < 7; i++<span style="color: #000000;"&gt;) { onCommands[i] </span>=<span style="color: #000000;"&gt; noCommand; offCommands[i] </span>=<span style="color: #000000;"&gt; noCommand; } } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> setCommand(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot,Command onCommand,Command offCommand) { onCommands[slot] </span>=<span style="color: #000000;"&gt; onCommand; offCommands[slot] </span>=<span style="color: #000000;"&gt; offCommand; } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> onButtonWasPushed(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot) { onCommands[slot].execute(); } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> offButtonWasPushed(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot) { offCommands[slot].execute(); } </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; String toString() { StringBuffer stringBuff </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StringBuffer(); stringBuff.append(</span>"\n----- Remote Control -----\n"<span style="color: #000000;"&gt;); </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < onCommands.length; i++<span style="color: #000000;"&gt;) { stringBuff.append(</span>"[slot" + i + "] " + onCommands[i].getClass().getName() + "\t\t" + offCommands[i].getClass().getName() + "\n"<span style="color: #000000;"&gt;); } </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; stringBuff.toString(); }

}

具体命令(太多了,不贴啦):

StereoOffWithCDCommand </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; StereoOffWithCDCommand(Stereo stereo) { </span><span style="color: #0000ff;"&gt;this</span>.stereo =<span style="color: #000000;"&gt; stereo; } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; execute() { stereo.off(); stereo.popCD(); }

}

测试一下:

= Light light </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Light(); GarageDoor garageDoor </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoor(); Stereo stereo </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Stereo(); LightOnCommand lightOnCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOnCommand(light); LightOffCommand lightOffCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOffCommand(light); GarageDoorOpenCommand garageDoorOpenCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoorOpenCommand(garageDoor); GarageDoorCloseCommand garageDoorCloseCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoorCloseCommand(garageDoor); StereoOnWithCDCommand stereoOnWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOnWithCDCommand(stereo); StereoOffWithCDCommand stereoOffWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOffWithCDCommand(stereo); remoteControl.setCommand(</span>0<span style="color: #000000;"&gt;,lightOnCommand,lightOffCommand); remoteControl.setCommand(</span>1<span style="color: #000000;"&gt;,garageDoorOpenCommand,garageDoorCloseCommand); remoteControl.setCommand(</span>2<span style="color: #000000;"&gt;,stereoOnWithCDCommand,stereoOffWithCDCommand); System.out.println(remoteControl); remoteControl.onButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.onButtonWasPushed(</span>1<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>1<span style="color: #000000;"&gt;); remoteControl.onButtonWasPushed(</span>2<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>2<span style="color: #000000;"&gt;); }

}

输出

----- Remote Control -----灯亮了
灯灭了
车库门开了
车库门关了
音响已打开
在音响中放入CD
音响声音调到11
音响已关闭
在音响中取出CD

重点来了,敲黑板!

有些按钮还有没有被分配命令,如果我们将其赋值为null的话,onButtonWasPressed()就要这样写:

onButtonWasPushed( (onCommands[slot] !=

为了减少判断的麻烦,可以为其付一个空命令,它是一个不做任何事情的对象,是一个空对象(null object)。

当你不想返回一个有意义的对象时,空对象就很有用。客户也可以将处理null的责任转移给空对象。

到目前为止,我们还有一个要求没有实现,那就是撤销功能,撤销上一条命令。

首先,修改Command接口:

然后修改遥控器类,很简单,只需要记录一下上一条命令就可以了。

<span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; RemoteControlWithUndo() {
    onCommands </span>= <span style="color: #0000ff;"&gt;new</span> Command[7<span style="color: #000000;"&gt;];
    offCommands </span>= <span style="color: #0000ff;"&gt;new</span> Command[7<span style="color: #000000;"&gt;];

    Command noCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; NoCommand();
    </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < 7; i++<span style="color: #000000;"&gt;) {
        onCommands[i] </span>=<span style="color: #000000;"&gt; noCommand;
        offCommands[i] </span>=<span style="color: #000000;"&gt; noCommand;
    }
    undoCommand </span>=<span style="color: #000000;"&gt; noCommand;
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> setCommand(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot,Command offCommand) {
    onCommands[slot] </span>=<span style="color: #000000;"&gt; onCommand;
    offCommands[slot] </span>=<span style="color: #000000;"&gt; offCommand;
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> onButtonWasPushed(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot) {
    onCommands[slot].execute();
    undoCommand </span>=<span style="color: #000000;"&gt; onCommands[slot];
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> offButtonWasPushed(<span style="color: #0000ff;"&gt;int</span><span style="color: #000000;"&gt; slot) {
    offCommands[slot].execute();
    undoCommand </span>=<span style="color: #000000;"&gt; offCommands[slot];
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; undoButtonWasPushed() {
    undoCommand.undo();
}

}

再次测试一下:

= Light light </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Light(); GarageDoor garageDoor </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoor(); Stereo stereo </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Stereo(); LightOnCommand lightOnCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOnCommand(light); LightOffCommand lightOffCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOffCommand(light); GarageDoorOpenCommand garageDoorOpenCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoorOpenCommand(garageDoor); GarageDoorCloseCommand garageDoorCloseCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoorCloseCommand(garageDoor); StereoOnWithCDCommand stereoOnWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOnWithCDCommand(stereo); StereoOffWithCDCommand stereoOffWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOffWithCDCommand(stereo); remoteControl.setCommand(</span>0<span style="color: #000000;"&gt;,stereoOffWithCDCommand); remoteControl.onButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.undoButtonWasPushed(); remoteControl.onButtonWasPushed(</span>1<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>1<span style="color: #000000;"&gt;); remoteControl.undoButtonWasPushed(); }

}

输出

很好,基本功能都实现了。

现在的遥控器只能撤销前一条命令,如果想要连续撤销,可以用一个栈来保存运行过的命令。

新的需求来了,现在希望按下一个按钮可以做许多事情,嗯……比如你从外面回家了,你想开灯,打开电视,打开音响……你想睡觉了,你要关灯关电视关音响。有没有办法,用一个按钮做一系列事情?

定义一个宏命令:

MacroCommand </span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; MacroCommand(Command[] commands) { </span><span style="color: #0000ff;"&gt;this</span>.commands =<span style="color: #000000;"&gt; commands; } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; execute() { </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < commands.length; i++<span style="color: #000000;"&gt;) { commands[i].execute(); } } </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; undo() { </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < commands.length; i++<span style="color: #000000;"&gt;) { commands[i].undo(); } }

}

测试:

= Light light </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Light(); GarageDoor garageDoor </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; GarageDoor(); Stereo stereo </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; Stereo(); LightOnCommand lightOnCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOnCommand(light); LightOffCommand lightOffCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; LightOffCommand(light); StereoOnWithCDCommand stereoOnWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOnWithCDCommand(stereo); StereoOffWithCDCommand stereoOffWithCDCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StereoOffWithCDCommand(stereo); Command[] onCommands </span>=<span style="color: #000000;"&gt; {lightOnCommand,stereoOnWithCDCommand}; Command[] offCommands </span>=<span style="color: #000000;"&gt; {lightOffCommand,stereoOffWithCDCommand}; MacroCommand onMacroCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; MacroCommand(onCommands); MacroCommand offMacroCommand </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; MacroCommand(offCommands); remoteControl.setCommand(</span>0<span style="color: #000000;"&gt;,onMacroCommand,offMacroCommand); remoteControl.onButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.offButtonWasPushed(</span>0<span style="color: #000000;"&gt;); remoteControl.undoButtonWasPushed(); }

}

命令可以将运算块打包(一个接收者和一组动作),然后将它传来传去,就像是一般的对象一样。

现在,即使在命令对象被创建许久之后,运算依然可以被调用

事实上,它甚至可以在不同的线程中被调用

我们可以利用这样的特性衍生一些应用

例如:“日程安排” “线程池” “工作队列” 等

工作队列和进行计算的对象之间完全是解耦的。

通过新增两个方法store()和load(),我们可以将所有的动作都记录在日志中,,并能在系统死机后,重新调用这些动作恢复到之前的状态。

  Command { execute(); undo(); store(); load(); } 

猜你在找的设计模式相关文章