文章目录
设计要求
小明接到了新任务,要求设计一个遥控器,这个遥控器上面有七个插槽,每个插槽都有对应的开和关按钮。要求每个都能控制任意家电如灯,电视,电风扇,门等等。以下是这些家电的类图
|
小明本想用if-else语句进行判断插槽slot上面的对象,在进行的对应,如
if(slot == light)
light.on()
else if(slot == sprinkler)
sprinkler.on()
但是很快小明就发现了问题。
- 假如未来有新的家电加入,就必须修改代码。违法了
对扩展开放,对修改关闭的设计原则
- 有些家电还具有自己特殊的方法,比如电视机有调节声音的setVolunm()方法。而现在无法支持除了on()和off()以外的方法。
小明发现,问题的关键在于,动作请求者和动作执行者紧紧地捆绑在了一起。请求者就是遥控器,执行者就是各种家电。能不能找个中间商,协调一下两者呢?
命令模式
小明查阅了设计模式葵花宝典后,发现还真的有个命令模式
,可以完美地实现动作请求者和动作执行者解耦的问题。在命令模式中,请求或者说命令被视为一种对象,用于沟通动作请求者和动作执行者。它具有以下几个角色
- 命令对象,用于传递命令
- 执行对象,用于执行对应的命令
- 请求对象,用于发出请求
-
Invoker
,提供使用命令对象的入口
基本流程是,请求对象创建一个命令对象,之后将命令对象存储在Invoker中,之后,客户通过Invoker执行命令。
它的类图如图所示
|
实际实现
掌握了命令模式后,小明就可以根据按照套路进行设计了。
命令对象的设计
先来一个命令接口
public interface Command {
public void execute();
}
再来一个控制灯泡打开的具体命令
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
}
遥控器的设计
这里我们设计一个最简单的遥控器,它只有一个插槽,负责打开一个装置。
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
简单测试
最后,来一个简单的测试。
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();
}
}
在上述的具体实现中,remote就是invoker,而command对应command,而RemoteControlTest代表client,最后light则是reciever。
命令模式具体应用
队列请求
命令模式可以处理队列请求,假设有一个工作队列,客户在一端添加命令,然后另外一端是线程,线程在队列中取出一个命令,调用它的execute方法,等待这个调用完成,然后将这个命令对象抛弃,再取出下一个命令。
日志请求
某些应用要求我们将所有的动作记录在日志中,并在系统死机后,调用这些动作恢复到原来的状态。命令模式可以把这些动作记录为一个个命令对象,持久化存储在磁盘中,一旦死机,我们就可以将这些命令对象重新加载并且调用它们的execute()方法。实现恢复。