什么是命令模式?
请求以命令的形式包裹在对象中,并传给调用对象。
调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
将请求、命令、动作等封装成对象,这样可以让项目使用这些对象来参数化其他对象,使得命令的请求者和执行者解耦。
网络异常,图片无法展示
|
项目背景
现在我们有一个家电自动化遥控器项目,要求设置遥控器各个按钮,控制家电的不同的设备。怎样设计自动化遥控器才能保证遥控器的扩展性好、维护性好呢?
网络异常,图片无法展示
|
代码示例
网络异常,图片无法展示
|
看上面的UML类型,我们定义了设备动作类,设备执行命令类,以及设备控制类。
设备动作类
首先是定义电器设备类。
灯类(可以开关):
public class Light { String loc = ""; public Light(String loc) { this.loc = loc; } // 开灯 public void on() { System.out.println(loc + " On"); } // 关灯 public void off() { System.out.println(loc + " Off"); } } 复制代码
音响类(可以开关,打开cd,调大和降低音量):
// 音响 public class Stereo { static int volume = 0; //调节音量 public void setVol(int vol) { volume = vol; System.out.println("Stereo volume=" + volume); } public int getVol() { return volume; } public void on() { System.out.println("Stereo On"); } public void off() { System.out.println("Stereo Off"); } // CD 播放 public void setCd() { System.out.println("Stereo setCd"); } } 复制代码
设备执行命令类
然后定义电器设备命令类。
设备命令抽象接口:
public interface Command { // 执行 void execute(); // 回退 void undo(); } 复制代码
不执行任何设备命令:
public class NoCommand implements Command { @Override public void execute() { } @Override public void undo() { } } 复制代码
关灯:
public class LightOffCommand implements Command { private Light light; public LightOffCommand(Light light) { this.light = light; } @Override public void execute() { light.off(); } @Override public void undo() { light.on(); } } 复制代码
开灯:
public class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light = light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); } } 复制代码
关音响:
public class StereoOffCommand implements Command { private Stereo setreo; public StereoOffCommand(Stereo setreo) { this.setreo = setreo; } @Override public void execute() { setreo.off(); } @Override public void undo() { setreo.on(); setreo.setCd(); } } 复制代码
开音响:
public class StereoOnCommand implements Command { private Stereo setreo; public StereoOnCommand(Stereo setreo) { this.setreo = setreo; } @Override public void execute() { setreo.on(); setreo.setCd(); } @Override public void undo() { setreo.off(); } } 复制代码
音响-增加音量:
public class StereoAddVolCommand implements Command { private Stereo setreo; public StereoAddVolCommand(Stereo setreo) { this.setreo = setreo; } @Override public void execute() { int vol = setreo.getVol(); if (vol < 11) { setreo.setVol(++vol); } } @Override public void undo() { int vol = setreo.getVol(); if (vol > 0) { setreo.setVol(--vol); } } } 复制代码
音响-音量降低:
public class StereoSubVolCommand implements Command { private Stereo setreo; public StereoSubVolCommand(Stereo setreo) { this.setreo = setreo; } @Override public void execute() { int vol = setreo.getVol(); if (vol > 0) { setreo.setVol(--vol); } } @Override public void undo() { int vol = setreo.getVol(); if (vol < 11) { setreo.setVol(++vol); } } } 复制代码
如果我们一个按钮想控制多个设备,定义如下类。
命令列表类:
public class MarcoCommand implements Command { private Command[] commands; public MarcoCommand(Command[] commands) { this.commands = commands; } @Override public void execute() { for (int i = 0, len = commands.length; i < len; i++) { commands[i].execute(); } } @Override public void undo() { for (int i = commands.length - 1; i >= 0; i--) { commands[i].undo(); } } } 复制代码
设备控制类
最后定义设备控制类。
按钮控制抽象接口:
public interface Control { // 按钮开 void onButton(int slot); // 按钮关 void offButton(int slot); // 按钮回退 void undoButton(); } 复制代码
具体设备控制类:
import java.util.Stack; public class CommandModeControl implements Control { private Command[] onCommands; private Command[] offCommands; // 将执行过得命令放入栈中 private Stack<Command> stack = new Stack<Command>(); //初始化所有按钮槽不执行任何操作 public CommandModeControl() { onCommands = new Command[5]; offCommands = new Command[5]; Command noCommand = new NoCommand(); for (int i = 0, len = onCommands.length; i < len; i++) { onCommands[i] = noCommand; offCommands[i] = noCommand; } } // 定义每个槽的打开按钮和关闭按钮 public void setCommand(int slot, Command onCommand, Command offCommand) { onCommands[slot] = onCommand; offCommands[slot] = offCommand; } // 执行打开操作 @Override public void onButton(int slot) { onCommands[slot].execute(); stack.push(onCommands[slot]); } // 执行关闭操作 @Override public void offButton(int slot) { offCommands[slot].execute(); stack.push(offCommands[slot]); } // 回退 @Override public void undoButton() { stack.pop().undo(); } } 复制代码
执行结果
命令测试:
public class ControlTest { public static void main(String[] args) { CommandModeControl control = new CommandModeControl(); Light bedroomlight = new Light("卧室灯"); Light kitchlight = new Light("厨房灯"); LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight); LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight); LightOnCommand kitchlighton = new LightOnCommand(kitchlight); LightOffCommand kitchlightoff = new LightOffCommand(kitchlight); // 批量操作灯的开关 Command[] oncommands = {bedroomlighton, kitchlighton}; Command[] offcommands = {bedroomlightoff, kitchlightoff}; MarcoCommand onmarco = new MarcoCommand(oncommands); MarcoCommand offmarco = new MarcoCommand(offcommands); Stereo stereo = new Stereo(); // 音响开关 StereoOnCommand stereoOn = new StereoOnCommand(stereo); StereoOffCommand stereoOff = new StereoOffCommand(stereo); // 音响音量调节 StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo); StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo); // 卧室灯 control.setCommand(0, bedroomlighton, bedroomlightoff); // 厨房灯 control.setCommand(1, kitchlighton, kitchlightoff); // 音响开关 control.setCommand(2, stereoOn, stereoOff); // 音响音量大小调节 control.setCommand(3, stereoaddvol, stereosubvol); // 批量操作灯的开关 control.setCommand(4, onmarco, offmarco); // 卧室开 control.onButton(0); // 卧室关 control.undoButton(); // 厨房开 control.onButton(1); // 厨房关 control.offButton(1); // 音响开 control.onButton(2); // 音响音量加 control.onButton(3); // 音响音量减 control.offButton(3); // 音响音量加 control.undoButton(); // 关音响 control.offButton(2); // 开音响 control.undoButton(); // 批量开灯 control.onButton(4); // 批量关关 control.offButton(4); } } 复制代码
输出结果:
卧室灯 On
卧室灯 Off
厨房灯 On
厨房灯 Off
Stereo On
Stereo setCd
Stereo volume=1
Stereo volume=0
Stereo volume=1
Stereo Off
Stereo On
Stereo setCd
卧室灯 On
厨房灯 On
卧室灯 Off
厨房灯 Off
总结
优点:
1、降低了系统耦合度。
2、新的命令可以很容易添加到系统中去。
缺点:
使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:
认为是命令的地方都可以使用命令模式,比如:
- GUI中每一个按钮都是一条命令。
- 模拟CMD。