命令模式
场景
小张的公司最近接到一个新的项目,要做一个遥控器。
有七个插槽,用来控制电灯,电视,主卧门….的打开与关闭并缺有一个undo 撤销按钮 能够退回上一次的操作。
电灯,电视的对象已经提供了。
小张的需求理解
小张看了遥控器的原型图和电灯…的类图觉得这个需求很好做,每个插槽存放各自的对象Light,Tv,….然后按钮调用具体的方法。
小张的简单实现
public class RemoteController{
Light light = new Light();
void buttonOnPress(){
light.on();
}
void buttonOffPress(){
light.off();
}
}
以此类推,小张为每个插槽都设计了和上面结构相同的对象。老王看了一眼,小张的设计说道,电灯这些对象是电灯厂商给我们的对象,现在遥控器和电灯的具体实现是紧密耦合的(还记得我们在策略模式的最后喊出的那句口号吗),并且undo呢?
餐厅的例子
老王告诉小张,这个需求如果使用命令模式能够做到解耦,并且undo也很好实现。
先来看看,这样一个场景。在餐厅吃饭时,顾客只需要根据菜单点好菜,然后服务员拿到点菜单送到厨房,然后厨师根据菜单做好饭菜,厨师按铃服务员把做好的饭菜送到对应的顾客手里。
这里可以看到 服务员是顾客和厨师的中间者也是协调者。如果顾客直接和厨师接触呢?厨师会忙的没有来炒菜。
服务员把顾客和厨师进行了解耦,有了服务员这个角色,顾客和厨师之间是松耦合的,并且厨师有变动也不会影响到顾客的就餐的,这就是松耦合的魅力。
有人说过:Any problem in computer science can be solved by anther layer of indirection.
计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决
命令模式
命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
回到遥控器的场景,我们需要解耦遥控器和电灯电视等,这样可以应对未来的改变。我们需要增加一个中间者来和电灯电视接触,并且此对象能够被遥控器简单的使用。
命令对象
/**
* 命令模式
*
* @author xuelongjiang
*
*/
public interface Command {
public void execute();
public void undo();
}
/**
*
* 打开灯 命令
*
* @author xuelongjiang
*/
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light){
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
/**
* 关闭灯命令
*
* @author xuelongjiang
*/
public class LightOffCommand implements Command {
Light light ;
public LightOffCommand(Light 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() {
}
@Override
public void undo() {
}
}
/**
* xuelongjiang
*
* 遥控器
*/
public class RemoteControl {
Command [] onCommands;//开
Command [] offCommands;// 关
Command undoCommand; //撤销
public RemoteControl(){
onCommands = new Command[7];
offCommands = new Command[7];
for(int i = 0;i<7;i++){
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
public void setCommand(int slot,Command onCommand,Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void on(int slot){
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void off(int slot){
offCommands[slot].execute();
undoCommand = offCommands[slot];
}
public void undo(){
undoCommand.undo();
}
}
至此可以看到,我们使用command解耦了电灯和遥控器,并且如果有新的灯泡或者遥控器需要更换控制对象,我们只需要再实现一个command。
这样就做到了对修改关闭,对扩展开放。
要点
- 命令模式将发出请求的对象和执行请求的对象解耦
- 在被解耦的两者之间是通过命令对象进行沟通的。