命令模式
封装调用方法,命令模式能够把方法调用封装到一个起来。这样,命令方法就不用在乎方法内部是做什么的了,只需要关心封装的过程,是开还是关闭某些东西。
它能做记录日志,或者撤销等。
家电自动化遥控器(命令模式详解)
现在我们有一个需求,就是帮家电厂商设计一个遥控器,这个遥控器有4个链接设备的开关,和一个撤销按钮。如下图所示:
现在我们看看厂商的家电类有什么特征:
第一个命令对象
首先我们要创建一个命令接口,是接口模式的基础:
public interface Command {
void execute();
}
所有命令对象都要包含实现execute这个方法,下面我们来实现一个Light()对象的开,命令对象吧。
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
这是一个Light对象的(开)命令对象。完成了第一个命令对象之后,我们来看看怎么使用该命令对象吧。假设我们有一个简单的遥控控制器,它只对应一个按钮和一个插槽。
public class SimpleRemoteControl {
private Command command;
public SimpleRemoteControl() {
}
public void setCommand(Command command) {
this.command = command;
}
//按下按钮的动作
public void buttonWasPressed(){
command.execute();
}
}
命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
命令模式的基础模型:
undo()方法在下面设计更复杂的命令模式的时候会讲到。
复杂的命令模式
下面是能够控制7个家电设备和一个撤回按钮的遥控器的代码:
public class RemoteControl {
Command[] onCommands; //开命令
Command[] offCommands; //关命令
Command undoCommand; //撤回命令
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command command = new NoCommand(); //初始化为什么都不做的命令对象
for (int i=0; i<7; i++){
onCommands[i] = command;
offCommands[i] = command;
}
undoCommand = command;
}
public void setCommand(int slot, Command onCommand, Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot){
onCommands[slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed(int slot){
offCommands[slot].execute();
undoCommand = onCommands[slot];
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("------test------");
for (int i=0; i<onCommands.length; i++){
sb.append("slot "+i+" "+onCommands[i].getClass().getName())
.append(" "+offCommands[i].getClass().getName());
}
return sb.toString();
}
}
下面是点灯开的命令:
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 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();
}
}
继承Common的命令:
public interface Command {
//执行
void execute();
//撤回
void undo();
}
undo就是命令的撤销方法,就像点灯的开命令,那么他的撤销功能当然是点灯的关啦。
灯对象:
public class Light {
private boolean flag; //点灯开关的标志位
public void on(){
System.out.println("light is on");
}
public void off(){
System.out.println("light is off");
}
}
测试方法:
public void test2(){
RemoteControl remoteControl = new RemoteControl();
Light light = new Light();
LightOnCommand lightOnCommand = new LightOnCommand(light);
LightOffCommand lightOffCommand = new LightOffCommand(light);
for (int i=0; i<5; i++){
//这里假设插槽控制了5个灯泡(为了方便,实际可以创建多几个家电类和命令),剩下两个没有插电器
remoteControl.setCommand(i, lightOnCommand, lightOffCommand);
}
System.out.println(remoteControl);
remoteControl.onButtonWasPushed(1);
remoteControl.offButtonWasPushed(1);
}
命令模式的更多用途
命令可以将运算块打包,然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被调用之后,运算依然可以被调用
工作队列
假设当前有一个命令队列,从一端添加需要完成的工作命令,在另一端是一个线程,负责取出线程,执行execute()方法。
总结
命令模式:将请求封装为对象,这可以让你使用不同的请求、队列、或者日志请求来参数化其他对象。命令模式也支持撤销操作。