假设要创建一个遥控器,该遥控器上有7个插槽(每个可以插上不同的家电),每个插槽对应了开关按钮,并且遥控器上还有一个撤销按钮用于撤销上一次的操作
假设一个顾客来到餐厅要进行点餐,整体流程如下:
- 顾客点了需要的食物(
createOrder
),生成了一个订单- 服务员拿走订单(
takeOrder
),放在订单柜台并通知厨师准备(orderUp
)- 厨师根据订单上的指示进行操作(
makeBurger
)- 做好后顾客就可以开吃了
takeOrder
中传入不同参数)然后调用订单上的orderUp
方法
- 命令模式将请求封装为对象,以便使用不同的请求、队列或日志来参数化其他对象
- 命令对象将动作和接收者包进对象中,该对象只暴露出
excute
方法,该方法被调用时接收者就进行这些动作,外部不知道进行了哪些动作,只需要知道调用excute
方法,请求的目的就能达成(比如插槽不需要知道插入的时什么电器,执行excute
方法就行)- 命令模式将发出请求的对象和执行请求的对象进行解耦,这两种通过命令对象进行交流
- 命令可以实现日志和事务系统
餐厅 | 命令模式 |
---|---|
服务员 | Invoker |
厨师 | Receiver |
orderUp() | execute() |
订单 | Command |
顾客 | Client |
takeOrder() | setCommand() |
该部分介绍大致设计步骤
// 可以理解为点餐的订单
public interface Command{
public void execute();
public void undo();
}
public class LightOnCommand implements Command{
Light light; // 有on和off方法(相当于厨师)
public LightOnCommand(Light light){
this.light = light;
}
// 该订单上写明要的餐(即要求厨师需要执行什么操作)
public void execute(){
light.on(); // 电灯变为接收者,负责接收请求
}
// 撤销就是做一个相反的操作
public void undo(){
light.off();
}
}
// 相当于服务员
public class SimpleRemoteControl{
Command slot;
public SimpleRemoteControl(){}
// 服务员拿走顾客下好的单
public void setCommand(Command command){
slot = command;
}
// 按下按钮则调用该方法(服务员把订单放在厨师台,让厨师开始执行订单)
public void buttonWasPressed(){
slot.execute();
}
}
// 相当于Client
public class RemoteControlTest{
public static void main(String[] args){
// 遥控器为调用者,传入一个命令对象然后发出请求
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light(); // 请求的接收者(厨师)
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn); // 把命令传递给调用者
remote.buttonWasPressed(); // 按下按钮
}
}
public class RemoteControl{
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl(){
onCommands = new Command[7]; // 7个开按钮
offCommands = new Command[7]; // 7个关按钮
// 初始化为一个具体对象(该对象中的excute方法不做任何动作)
// 这样就不需要每次都先判断onCommands[slot]是否为null再去执行excute方法
Command noCommand = new NoCommand();
for(int i = 0; i < 7; i++){
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
// 三个参数为:插槽的位置,开命令,关命令
public void setCommand(int slot, Command onCommand, Command offCommand){
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
// 按下开按钮后,就开始执行一系列的动作
public void onButtonWasPushed(int slot){
onCommands[slot].excute();
undoCommand = onCommands[slot]; // 按下按钮后将该操作其记录,需要撤销时可以使用
}
// 按下关按钮后,就开始执行一系列的动作
public void offButtonWasPushed(int slot){
offCommands[slot].excute();
undoCommand = offCommands[slot]; // 按下按钮后将该操作其记录,需要撤销时可以使用
}
// 按下撤销按钮后,就开始执行上一个命令的一系列动作
public void undoButtonWasPushed(int slot){
undoCommand.undo();
}
}
public class NoCommand implements Command{
public void excute(){} // 什么事都不做(可以理解为插槽上没有插入家电,但是依旧可以按下按钮)
public void undo(){} // 同理
}
public class RemoteLoader{
public static void main(String[] args){
RemoteControl remoteControl = new RemoteControl();
Light roomLight = new Light("room");
LightOnCommand roomLightOn = new LightOnCommand(roomLight);
LightOffCommand roomLightOff = new LightOffCommand(roomLight);
remoteControl.setCommand(0, roomLightOn, roomLightOff);
remoteControl.onButtonWasPushed(0); // 使用插槽0打开卧室的灯
remoteControl.offButtonWasPushed(0); // 使用插槽0关闭卧室的灯
remoteControl.undoButtonWasPushed(0); // 按下撤销按钮,卧室灯重新打开
}
}
假设此时想按下一个插槽0的开按钮后,同时关闭卧室灯并且打开电视,该如何实现?
// 包含一系列命令的命令
public class MarcoCommand implements Command{
Command[] commands;
public MarcoCommand(Command[] commands){
this.commands = commands;
}
public void execute(){
for(int i = 0; i < commands.length; i++){
commands[i].excute();
}
}
public void undo(){
for(int i = 0; i < commands.length; i++){
commands[i].undo();
}
}
}
// 具体实现
Light roomLight = new Light("room");
TV tv = new TV("tv");
LightOffCommand roomLightOff = new LightOffCommand(roomLight);
TVOnCommand tvOn = new TVOnCommand(tv);
Commands[] roomLightOntvOff = {roomLightOff, tvOn};
MarcoCommand demo = new MarcoCommand(roomLightOntvOff);
remoteControl.setCommand(0, demo, null);