命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也可支持可撤销的操作。
有一个这样的业务逻辑,在客厅的进门处有一个开关面板,该面板上有两个开关,第一个开关是打开客厅的灯,第二个开关是打开客厅的电视。下面就来具体实现一下。
首先实现电视与灯:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Light { String location = "";
public Light(String location) { this.location = location; }
public void on() { System.out.println("打开"+location+"的灯"); }
public void off() { System.out.println("关闭"+location+"的灯"); } }
|
1 2 3 4 5 6 7 8 9 10
| public class Television {
public void open() { System.out.println("打开电视"); }
public void close() { System.out.println("关闭电视"); } }
|
硬编码的方式实现开关面板功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class SwitchControlOld { Light light = new Light("客厅"); Television television = new Television();
public void onButtonWasPressed(int solt) { if(solt==0){ light.on(); }else if(solt==1){ television.open(); } }
public void offButtonWasPressed(int solt) { if(solt==0){ light.off(); }else if(solt==1){ television.close(); } } }
|
从上面可以看到,实现得十分的不友好,如果开关面板上再加个开关按钮就必须得再加一组判断。而且,开关控制面板跟具体的电器开关逻辑强耦合。
使用命令模式重新实现该逻辑
1 2 3 4 5 6 7
| public interface Command { public void execute(); public void undo(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class TelevisionOnCommand implements Command { Television television;
public TelevisionOnCommand(Television television) { this.television = television; } @Override public void execute() { television.open(); } @Override public void undo() { television.close(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class TelevisionOffCommand implements Command { Television television;
public TelevisionOffCommand(Television television) { this.television = television; } @Override public void execute() { television.close(); } @Override public void undo() { television.open(); } }
|
使用命令模式实现的开关控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class SwitchControl { Command[] onCommands = new Command[2]; Command[] offCommands = new Command[2]; public void setCommand(int solt,Command onCommand,Command offCommand) { onCommands[solt] = onCommand; offCommands[solt] = offCommand; } public void onButtonWasPressed(int solt) { onCommands[solt].execute(); } public void offButtonWasPressed(int solt) { onCommands[solt].undo(); } }
|
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Test { public static void main(String[] args) { SwitchControl control = new SwitchControl(); Light light = new Light("客厅"); control.setCommand(0,new LightOnCommand(light),new LightOffCommand(light)); control.onButtonWasPressed(0);
Television television = new Television(); control.setCommand(1,new TelevisionOnCommand(television),new TelevisionOffCommand(television)); control.onButtonWasPressed(1); } }
|
从上面的代码中可以看到,对于开关控制面板来讲,它只需要按下开关并执行命令的execute方法,不需要知道每个电器的具体实现。
通过使用命令模式对代码的改造,让开关控制器跟具体电器的开/关彻底的解耦。如果想要在控制器上增加新的按钮,可以很方便的增加上去。使得代码更加清晰,易于扩展与维护。
从命令模式的特性中可以看出,如果此时的需求变为按下一个开关需要同时打开灯与电视,那么只需要一个command数组即可。把LightOnCommand和TelevisionOnCommand放入其中,遍历调用execute。
另外,在队列请求中大量的用到了命令模式。一个个的请求依次而来,接收端不需要知道每个请求的具体逻辑,只需要依次调用请求的execute方法即可完成请求的处理。
上一篇:设计模式系列之五工厂模式
下一篇:设计模式系列之七适配器模式
留言
欢迎交流想法。留言会通过 GitHub Issues 保存,首次使用需要登录 GitHub。