命令模式定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
初看到定义时觉得好抽象,“请求”是个什么。
“一个技术往往是伴一个问题的出现而出现的”,我们应该想,为什么会出现这个模式?这个模式究竟有什么好处?
书中首先提出的一个需求:“公司想要设计一个家电自动化遥控器的API,公司有一些厂商类(如电灯、电视),还有一个遥控器,遥控器有七个可编程的插槽(每个插槽都可以指定一个不同的家电装置),每个插槽都有对应的开关按钮,还有一个撤销按钮。”
拿到需求时就想 厂商类如果就七个,那好,我直接把命令写到按钮里 。可是厂商类不只是七个,有十几个甚至会上百个。如果写好几个遥控器,那还能叫智能遥控器吗?所以不能这么设计。再者面向对象思想也不提倡这样做。那怎么办呢?
书中用餐厅来引出命令模式,在餐厅中
- 顾客把订单写好交给服务员
- 服务员把订单交给厨师
- 厨师看到订单后做好大餐
这其中,服务员获悉顾客想要什么,通过订单告诉厨师做什么。
服务员只知道厨师会做餐,不管厨师怎么去做。
我们可以这样想,服务员知道厨师会做餐(服务员拿着订单通知厨师),服务员喊一声“订单来了”(服务员调用orderUp()),厨师听到喊声看到订单后准备做餐(根据接收的指令准备)。
不管是哪个服务员哪个厨师,服务员把订单给厨师后,厨师也知道做什么,厨师不管谁订的还是谁送来的。这样订单把顾客与厨师就隔离了。
这里我们把顾客当作“发出请求的对象”,把厨师当作“接收与执行请求的对象”,而订单不就是“包含了请求的一个对象”。
回到我们的需求中,此时我们是否可以将厂商类(灯、电视)当作厨师(接收与执行请求的对象),将遥控器当作订单(包含了请求的一个对象)
这里的“请求”就是我们的一个命令。
这里命令模式就出现了,我们要控制灯,就要写一个控制灯的命令。要控制电视,就要写一个控制电视的命令。所以命令中包含了一个“接收与执行请求对象”。然后我们要写很多命令,可是我们的遥控器中不可能要包含每个命令。所以这里我们要创建一个命令接口,这个接口中有一个执行的方法(execute()…),每个命令要实现这个接口,然后在执行方法中完成“接收者”要做的动作。
相关代码如下:
灯类(厂商类)
public class Light {
public void off() {
System.out.println("Light is off!");
}
public void on() {
System.out.println("Light is on!");
}
}
命令接口
/**
*命令接口
**/
public interface Command{
public void execute();
}
实现命令接口的控制灯的命令
/**
*控制灯的命令(开灯)
**/
public class LightOnCommand implements Command{
//命令中持有一个灯对象
Light light;
public LightOnCommand(Light light){
this.light=light;
}
//实现接口中的execute()方法
public void execute(){
//开灯
light.on();
}
}
控制装置(遥控器)
public class SimpleRemoteControl{
//包含命令
Command command;
public SimpleRemoteControl(){}
//设置接收命令
public void setCommand(Command command){
this.command=command;
}
//按钮方法,按下按钮时执行动作
public void buttonWasPressed(){
//调用命令的执行方法
this.command.execute();
}
}
测试顾客
public class RemoteControlTest{
public static void main(String[] agrs){
//实例化灯(接收者)
Light light = new Light();
//实例化开灯命令类(包含一个灯的对象)
LightOnCommand lightOnCommand = new LightOnCommand (light);
//实例化控制装置(遥控器)
SimpleRemoteControl simpleRemoteControl = new SimpleRemoteControl();
//将开灯命令装入控制装置
simpleRemoteControl.setCommand(lightOnCommand );
//执行方法(开灯)
simpleRemoteControl.buttonWasPressed();
}
}
输出结果:
Light is on!
到此,命令模式已经实现原型了。遥控器只能控制七个装置,用命令模式我们就能很方便的更换装置了,而不用更改遥控器。
这其实就遵循了据说的设计原则:对扩展开放,对修改关闭。针对接口编程不要针对实现编程。