一个遥控器问题
假设我们有一个遥控器,这个遥控器上有多组的开关按键,每组按键对应着一个卡槽,卡槽是可编程的,能够控制一个电器。而我们有很多的电器,那么我们应该怎样设计这个遥控器呢?
一个无脑的方法当然就是对于每个卡槽,我们直接设置它为指定电器的开关。
1 | public class SimpleRemoteControl { |
代码归档位置如下:
https://github.com/changyuanchn/DesignPattern/tree/main/src/com/changyuan/CommandPattern
但是这样做有什么问题呢?最大的问题就是可扩展性。假设我们需要更换设备呢?就需要更改SimpleRemoteControl方法,这必然违背了我们的对修改关闭,对扩展开放的原则。
那么我们有什么方式将其分离呢?这里引入命令模式
命令模式
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他的对象。命令模式也支持可撤销的操作
什么意思呢?我们看上面的代码,这里面light/tv都是具体的对象,从前面的设计模式经验知识我们知道,对付这种,明显可以用接口来解决。可以light/tv/……等等的这些可以抽象出公共的什么接口呢?实际上我们用到了这些类实例对象了么?没有。用到的是他们的开关命令,所以我们可以抽象出command接口,然后创建具体的light类的请求command,然后将light实例对象传递给light类的请求command(在这个请求command中,实际使用的light类里面的开关操作)。这样我们的遥控器就不需要关心具体的类实例了,只需要调用command中的方法就可以了。
1 | public class Light { |
1 | public interface Command { |
1 | public class SimpleRemoteContrelTest { |
这是一个以light为例子的简单的打开按钮操作。我们创建了SimpleRemoteControl,它只关系请求。而我们的请求LightOnCmd只跟light有关。而SimpleRemoteControl中的buttonPressed操作,只需要调用Command的execute函数就可以了,具体的execute的调用关系就由子类自己处理了。
下面我们扩展上面的应用,我们给遥控器增加多个卡槽,以便于遥控器控制多个设备,然后增加关操作。
首先创建相应的设备
1 | public class Light { |
然后创建相应的命令
1 | public class LightOnCommand implements Command { |
然后设置控制器
1 | public class RemoteControl { |
测试代码如下:
1 | public class RemoteLoder { |
相对比最初的代码,增加了多个控制器,同时实现了关的操作。
设计图
添加撤销操作
这个实现比较简单,需要在Command接口中增加undo方法,然后Command子类实现这个方法,当undoButtonPressed时候,调用undo方法即可。跟上面添加开关的方法逻辑是一致的。1
2
3
4public interface Command {
public void execute();
public void undo();
}
增加一点难度:使用状态实现撤销
对于电灯的应用,就两个状态,要么开要么关,这个实现撤销是很容易的。但是如果一个设备有多个状态,这个要实现撤销就没那么容易了。
在上面代码上实现小改动就可以了。
代码如下:
再增加一点难度,我们要求一个按键可以同时打开所有的设备
代码如下
命令模式的更多用途
队列请求
日志请求