案例
我们需要开发一个遥控器,遥控器上的按钮可以开关电灯和空调,这个需求通常的实现如下伪代码:
if(buttonPressed == button1){
lights.on()
}else if(buttonPressed == button2){
airConditioner.on()
}
遥控器类 RemoteControl 耦合每个按钮关联的业务实现,比如上面的例子耦合了 light 和 airConditioner,如果业务有新需求需支持电视的控制,类 RemoteControl 的复杂度和耦合度逐步增加,那如何化解复杂度和解耦类 RemoteControl 呢?此时可以用命令模式来解决。
命令模式
官方定义:将一个请求封装成一个对象,然后你可以用不同的请求去参数化其他的对象,还可以排队或者记录请求,以及支持可撤销的操作。
官方定义比较抽象,难以理解,后面我们重构上面的例子,同时解释这个定义。
命令模式的主要概念
- Command
声明执行操作的接口 - ConcreteCommand
命令接口的实现类,会引用另外一个类来执行具体的业务逻辑,这个类称为接收者 - Client
创建一个具体命令对象并设定它的接收者 - Invoker
调用命令执行请求 - Receiver
命令的具体业务的服务提供者,任何类都可以当接收者
具体实现
首先定义接口和类,如下:
接口 Command
和开关灯命令实现类 LightOnCommand
,LightOffCommand
以及空调开关命令实现类 AirConditionerOnCommand
, AirConditionerOffCommand
,这些类的对象就是定义提到的封装请求的对象。
接收者类 Light
和 AirConditioner
遥控器类 RemoteControl
public interface Command {
void execute();
}
public class Light {
public void on(){
System.out.println("开灯");
}
public void off(){
System.out.println("关灯");
}
}
public class LightOnCommand implements Command{
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class LightOffCommand implements Command{
private final Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
this.light.off();
}
}
public class AirConditioner {
public void on(){
System.out.println("开空调");
}
public void off(){
System.out.println("关空调");
}
}
public class AirConditionerOnCommand implements Command{
private final AirConditioner airConditioner;
public AirConditionerOnCommand(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
}
@Override
public void execute() {
this.airConditioner.on();
}
}
public class AirConditionerOffCommand implements Command {
private final AirConditioner airConditioner;
public AirConditionerOffCommand(AirConditioner airConditioner) {
this.airConditioner = airConditioner;
}
@Override
public void execute() {
this.airConditioner.off();
}
}
public class RemoteControl {
private Command btn;
public void setBtn(Command btn) {
this.btn = btn;
}
public void buttonWasPressed(){
btn.execute();
}
}
public class RemoteControlTest {
public static void main(String[] args) {
RemoteControl remoteControl = new RemoteControl();
Light light = new Light();
AirConditioner airConditioner = new AirConditioner();
remoteControl.setBtn(new LightOnCommand(light));
remoteControl.buttonWasPressed();
remoteControl.setBtn(new LightOffCommand(light));
remoteControl.buttonWasPressed();
remoteControl.setBtn(new AirConditionerOnCommand(airConditioner));
remoteControl.buttonWasPressed();
remoteControl.setBtn(new AirConditionerOffCommand(airConditioner));
remoteControl.buttonWasPressed();
}
}
运行日志:
开灯
关灯
开空调
关空调
总结
从上面实现中可以看到,用接口 Command
解耦了遥控器和具体动作执行者,遥控器上的按钮不必关心具体动作执行者,而且按钮可以动态更换命令对象来操作不同设备,从这点也解释定义提到的,用封装的对象来参数化其他的对象,这里的遥控器的按钮就是被Command 参数化了, 将来新增设备时只需新增Command的实现类,符合开闭原则
。
此实现还有一个优点,简化了 RemoteControl
的实现,此类不在依赖或导入类 Light
, AirConditoner
。