1.这一节我们的任务是创建一个类似智能家居的万能遥控器,控制各种家电。我们需要将“请求”封装成对象(一个命令对象通过在特定接收者上绑定一组动作来封装请求),以便使用不同的请求、队列、或者日志来参数化其对象——这就是命令模式。
2.我们具体来看一个例子:
首先我们要完成对命令的对象封装:
只有一个方法,所有的具体命令的对象都要实现这个接口,这就做到了封装,比如对于灯这个对象,
我们可以通过上述接口封装“开灯”这个命令,这个就是所谓的命令对象,它把动作和接收者包进对象之中,只暴露一个execute方法:
而我们的遥控器对于上述封装要一无所知,这样才能做到解耦:
我们现在试着用一下这个遥控器,我们首先创建一个遥控器,然后创建开灯这个命令并置于其中,最后按下按键,这样,遥控器不知道是哪个对象(实际上是灯)进行了哪个动作(实际上是开灯这个动作)就可以完成请求的发出。如此一来,遥控器和灯之间的耦合性就非常低了:
很简单的,我们想要在一个遥控机中实现控制多个家电的能力就可以用一个数组来维护这样的一组命令,可以看做提供了多个命令的插槽:
你可能会注意到一个叫做noCommand的对象出现在初始化遥控器对象时。这个是个小技巧,我们并不想每次都检查某个插槽是不是都绑定了命令,那么我们就引入了这个东东,实现一个不做事情的命令:
这样初始化就让每一个插槽都有命令了,以后若不进一步指明命令的插槽,那么就是默认这个noCommand对象。这就是一个典型的“空对象”例子,当你不想返回一个有意义的对象时,空对象就十分有用,此时空对象可以做成判断NULL或者提示“未绑定”信息的工作。
3.我们在命令模式中也可以设置类似undo的撤销命令来撤销发出的命令请求:
我们还是拿开电灯这个操作来说明,毕竟这足够简单:对于一个开灯命令,其对应的撤销命令自然是“关电灯”:
虽然这简单之极,这还没完,我们必须让遥控器记住上次到底做了什么操作,才可能去撤销:
对于电灯,我们只有两种状态,但是要是多个状态呢?我们举一个吊扇的例子,吊扇有多个转速,高中低关,四个状态:
那么在实现风扇各个转速命令时,就要去记录在执行这个命令前风扇的转速,其撤销命令中则根据记录的部分完成undo操作。
当然你也可以实现诸如多个命令批量执行和完成多个撤销操作。
命令模式可以用于工作队列和日志操作等方面。