这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
什么是命令模式?
请求以命令的形式包裹在对象中,并传给调用对象。
调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
将请求、命令、动作等封装成对象,这样可以让项目使用这些对象来参数化其他对象,使得命令的请求者和执行者解耦。
项目背景
现在我们有一个家电自动化遥控器项目,要求设置遥控器各个按钮,控制家电的不同的设备。怎样设计自动化遥控器才能保证遥控器的扩展性好、维护性好呢?
代码示例
看上面的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。
注意:
如系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END