命令模式
什么是命令模式
命令模式是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作。
原理图
为什么要用命令模式
还记得在外观模式中我们所提到的智慧生活吗?
我们来仔细想一下这个逻辑,智慧助手相当于一个遥控器,用于发出命令,让接收端(具体智能家居)做出相应的反应。
在我们当时的实现中,只是体现出了智慧助手的统一调用,但并没有体现出“命令”,现在,让我们来体现一下“命令”。
假设现在只有灯和空调两个智能家居:
public class Lamp {
public void turnOn() {
System.out.println("开灯");
}
public void turnOff() {
System.out.println("关灯");
}
}
public class AirConditioner {
public void turnOn() {
System.out.println("开空调");
}
public void turnOff() {
System.out.println("关空调");
}
}
要有一个命令接口:
public interface ICommand {
void execute(); //执行
void undo();//撤销
}
对应操作有对应具体命令:
public class LampOnCommand implements ICommand {
private Lamp lamp;
public LampOnCommand(Lamp lamp) { //给命令设置一个接收器。
this.lamp = lamp;
}
@Override
public void execute() { //执行操作
lamp.turnOn();
}
@Override
public void undo() { //后悔,不做
lamp.turnOff();
}
}
public class LampOffCommand implements ICommand {
private Lamp lamp;
public LampOffCommand(Lamp lamp) {
this.lamp = lamp;
}
@Override
public void execute() {
lamp.turnOff();
}
@Override
public void undo() {
lamp.turnOn();
}
}
public class AirConditionerOnCommand implements ICommand {
private AirConditioner airConditioner;
public AirConditionerOnCommand(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
}
@Override
public void execute() {
airConditioner.turnOn();
}
@Override
public void undo() {
airConditioner.turnOff();
}
}
public class AirConditionerOffCommand implements ICommand {
private AirConditioner airConditioner;
public AirConditionerOffCommand(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
}
@Override
public void execute() {
airConditioner.turnOff();
}
@Override
public void undo() {
airConditioner.turnOn();
}
}
为了避免判空,做一个空命令:
//在调用时,存储的undoCommand默认是空命令,即使调用也无事发生,避免了对空引用的判断
public class NullCommand implements ICommand {
@Override
public void execute() {}
@Override
public void undo() {}
}
智慧助手:
public class SmartAssistant {
private List<ICommand> turnOnCommands; //存储开命令
private List<ICommand> turnOffCommands; //存储关命令
private ICommand undoCommand = new NullCommand(); //存储上一条命令,默认为空
public SmartAssistant() {
this.turnOnCommands = new ArrayList<>();
this.turnOffCommands = new ArrayList<>();
}
public void setCommand(int number, ICommand turnOnCommand, ICommand turnOffCommand) {
if (number >= turnOnCommands.size()){//防止越界
turnOffCommands.add(turnOffCommand);
turnOnCommands.add(turnOnCommand);
number = turnOffCommands.size()-1;
}
else{
turnOnCommands.set(number, turnOnCommand);
turnOffCommands.set(number,turnOffCommand);
}
System.out.println("对"+number+"号按键设置命令");
}
public void clickOnButton(int number){ //点击开按钮
turnOnCommands.get(number).execute();
undoCommand = turnOnCommands.get(number);
}
public void clickOffButton(int number){//点击关按钮
turnOffCommands.get(number).execute();
undoCommand = turnOffCommands.get(number);
}
public void undo(){//撤销,这里可以做一个栈用于维护undo命令,为简化演示未做实现。
undoCommand.undo();
undoCommand = new NullCommand();
}
}
进行调用:
public class Person {
public static void main(String[] args) {
Lamp lamp = new Lamp();
AirConditioner airConditioner = new AirConditioner();
SmartAssistant smartAssistant = new SmartAssistant();
//设置按键(连接智慧家居)
smartAssistant.setCommand(0,new LampOnCommand(lamp),new LampOffCommand(lamp));
smartAssistant.setCommand(1,new AirConditionerOnCommand(airConditioner),new AirConditionerOffCommand(airConditioner));
//点击
smartAssistant.clickOnButton(0);
smartAssistant.clickOffButton(0);
smartAssistant.clickOnButton(1);
smartAssistant.clickOffButton(1);
//撤销
smartAssistant.undo();
}
}
运行结果:
对0号按键设置命令
对1号按键设置命令
开灯
关灯
开空调
关空调
开空调
这样,我们通过一个ICommand的接口,实现的命令的传输,同时有了后悔的选择。
相比于在外观模式中的实现方式而言,更符合现实,智慧助手是通过发命令,而非直接找到家居做操作的方式实现智慧生活。