命令模式
定义
将"请求"封装成对象,以便使用不同的请求、队列、或者日志来参数化其他对象(就是命令对象拥有相同的接口,不同的命令对象可以被设置到调用者中)。命令模式也可以支持撤销操作。
-
一个命令对象将动作和接受者封装到命令对象中,这个对象只保留出一个execute()方法,当此方法被调用时,接收者就会进行这些动作。从外面看,其他对象不知道究竟哪个接收者进行了哪些动作。
-
一个调用者可以设置不同的请求。
-
命令模式将发出请求的对象和接收请求的对象解耦。被解耦的两者是通过命令对象进行沟通的,命令对象封装了接收者和动作(1个或多个)。
-
调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用。
-
调用者可以接受命令对象当做参数,甚至在运行时设置不同的命令对象。
-
命令对象可以支持撤销,通过实现undo()方法,让系统回到execute()执行之前的状态
示例
/**
* SimpleRemoteControlTest是命令模式的客户client
* @author huangy on 2019-05-25
*/
public class SimpleRemoteControlTest {
public static void main(String[] args) {
// 遥控器就是调用者
SimpleRemoteControl simpleRemoteControl = new SimpleRemoteControl();
Light ligh = new Light();
LightOnCommand lightOnCommand = new LightOnCommand(ligh);
// 把命令传递给调用者
simpleRemoteControl.setSlot(lightOnCommand);
// 模拟按下按钮
simpleRemoteControl.buttonWasPressed();
}
}
/**
* 简单遥控器
* @author huangy on 2019-05-25
*/
public class SimpleRemoteControl {
// 插槽
Command slot;
// 把命令对象设置到插槽上面
public void setSlot(Command slot) {
this.slot = slot;
}
public void buttonWasPressed() {
slot.execute();
}
}
/**
* 所有命令对象的通用接口
* @author huangy on 2019-05-25
*/
public interface Command {
/**
* 执行命令
*/
void execute();
}
/**
* @author huangy on 2019-05-25
*/
public class LightOnCommand implements Command {
public LightOnCommand(Light light) {
this.light = light;
}
/**
* 电灯
* 动作的接收者
*/
private Light light;
@Override
public void execute() {
light.on();
}
}
/**
* 电灯
* @author huangy on 2019-05-25
*/
public class Light {
public void on() {
System.out.println("Light on");
}
}
宏命令
宏命令是命令的简单延伸,允许调用多个命令,并且支持撤销。
/**
* 宏命令
* @author huangy on 2019-05-25
*/
public class MacroCommand implements Command {
Command[] commands;
Deque<Command> deque;
public MacroCommand(Command[] commands) {
this.commands = commands;
deque = new LinkedList<>();
}
@Override
public void execute() {
for (Command command : commands) {
command.execute();
deque.push(command);
}
}
@Override
public void undo() {
while (!deque.isEmpty()) {
// 利用栈实现撤回
Command command = deque.pop();
command.undo();
}
}
}
用途
队列请求
将命令对象放到队列中,然后另外一个线程从队列中获取命令对象,并且执行excute()方法
日志请求
把系统每一个动作当成命令对象,存储在磁盘中,一旦系统死机,可以将命令对象重新加载,并成批依次调用这些对象execute()方法。
命令也可以用来实现日志和事务系统。
命令模式如何应用在事务中?