命令模式将请求封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
现在有如下图遥控器。有3个可编程的插槽,以及对应的按钮。黑色按钮为开关按钮,有按下和弹起,两种状态。红色按钮为撤销按钮,撤销上一次操作,只有一种状态,按下立即弹起。
现在有两个电器。
// 电灯对象
public class Light {
public void on(){
System.out.println("light on ...");
}
public void off(){
System.out.println("light off ...");
}
}
// 音响对象
public class Stereo {
private String name;
private int volume;
public void on() {
System.out.println("stereo on ...");
}
public void off() {
System.out.println("stereo off ...");
}
public void insertCD(String name) {
this.name = name;
System.out.println("CD " + name + " insert ...");
}
public void takeCD() {
this.name = "";
System.out.println("CD " + name + " take ...");
}
public void setVolume(int volume) {
this.volume = volume;
System.out.println("volume set " + volume);
}
}
类图
我们通过编程来让遥控器控制电器。
// 命令接口
public interface Command {
void execute();
void undo();
}
// 空命令
public class NoCommand implements Command {
@Override
public void execute() {
System.out.println("command is null");
}
@Override
public void undo() {
System.out.println("command is null");
}
}
// 开启电灯命令
public class LightOn implements Command {
private Light light;
public LightOn(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
// 电灯关闭命令
public class LightOff implements Command {
private Light light;
public LightOff(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
// 打开音响命令
public class StereoOn implements Command {
private Stereo stereo;
public StereoOn(Stereo stereo) {
this.stereo = stereo;
}
@Override
public void execute() {
stereo.on();
stereo.insertCD("XXX CD");
stereo.setVolume(10);
}
@Override
public void undo() {
stereo.setVolume(0);
stereo.takeCD();
stereo.off();
}
}
// 关闭音响命令
public class StereoOff implements Command {
private Stereo stereo;
public StereoOff(Stereo stereo) {
this.stereo = stereo;
}
@Override
public void execute() {
stereo.setVolume(0);
stereo.takeCD();
stereo.off();
}
@Override
public void undo() {
stereo.on();
stereo.insertCD("XXX CD");
stereo.setVolume(10);
}
}
// 宏命令
public class Macro implements Command {
private List<Command> list;
public Macro(List<Command> list) {
this.list = list;
}
@Override
public void execute() {
for (Command command : list) {
command.execute();
}
}
@Override
public void undo() {
for (Command command : list) {
command.undo();
}
}
}
// 遥控器
public class RemoteControl {
// 遥控器上插槽的数量
private int num = 3;
private Command[] onCommands;
private Command[] offCommands;
private Stack<Command> historyCommands;
public RemoteControl() {
onCommands = new Command[num];
offCommands = new Command[num];
historyCommands = new Stack<>();
Command noCommand = new NoCommand(); // 所有插槽初始化为同一个空对象
for (int i = 0; i < num; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
if (slot < num && slot > -1) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
} else {
throw new ArrayIndexOutOfBoundsException("slot is not exist");
}
}
// 按下按钮
public void buttonDown(int slot) {
if (slot < num && slot > -1) {
onCommands[slot].execute();
historyCommands.push(onCommands[slot]);
} else {
throw new ArrayIndexOutOfBoundsException("slot is not exist");
}
}
// 按钮弹起
public void buttonUp(int slot) {
if (slot < num && slot > -1) {
offCommands[slot].execute();
historyCommands.push(offCommands[slot]);
} else {
throw new ArrayIndexOutOfBoundsException("slot is not exist");
}
}
// 撤销按钮
public void undo() {
if (historyCommands.isEmpty()) {
System.out.println("do not have history command");
} else {
historyCommands.pop().undo();
}
}
}
// 测试类
public class Test {
public static void main(String[] args) {
new Test().test();
}
private void test() {
// 遥控器对象
RemoteControl remoteControl = new RemoteControl();
// 各种家电
Light light = new Light();
Stereo stereo = new Stereo();
// 命令
LightOn lightOn = new LightOn(light);
LightOff lightOff = new LightOff(light);
StereoOn stereoOn = new StereoOn(stereo);
StereoOff stereoOff = new StereoOff(stereo);
// 宏命令
List<Command> onCommands = new ArrayList() {
{
add(lightOn);
add(stereoOn);
}
};
Macro macroOn = new Macro(onCommands);
List<Command> offCommands = new ArrayList() {
{
add(lightOff);
add(stereoOff);
}
};
Macro macroOff = new Macro(offCommands);
// 插入命令
remoteControl.setCommand(0, lightOn, lightOff);
remoteControl.setCommand(1, stereoOn, stereoOff);
remoteControl.setCommand(2, macroOn, macroOff);
// 操作遥控器
remoteControl.buttonDown(0);
remoteControl.buttonUp(0);
remoteControl.buttonDown(1);
remoteControl.buttonUp(1);
remoteControl.buttonDown(2);
remoteControl.buttonUp(2);
System.out.println(11111);
remoteControl.undo();
remoteControl.undo();
remoteControl.undo();
}
}
注:
- 我们把所有的电器的操作封装,暴露execute和undo方法。现在遥控器可以很方便的统一操作各种电器。
- 将发出请求的对象和接受请求的对象解耦
- 可以用来实现队列、日志、事务等
- 队列:一个线程负责把命令封装放入对序列中,另一个线程从队列中取出命令,调用execute方法
- 日志:把每次的操作记录下来,出现异常时读取日志,重新执行被记录的操作
参考文章
1. Head First 设计模式