设计模式(六):控制台中的“命令模式”(Command Pattern)

时间:2021-12-15 21:53:26

今天的博客中就来系统的整理一下“命令模式”。说到命令模式,我就想起了控制台(Console)中的命令。无论是Windows操作系统(cmd.exe)还是Linux操作系统(命令行式shell(Command Line Interface shell ,即CLI shell)都有命令行程序。说白了就是你输入你要执行的命令提示符,然后计算机就还是根据你所下达的命令来执行。你最终看到的是命令执行后的结果,具体的执行细节不需要你一步步的去下达命令。(与之前博客保持一致,我们仍然使用Swift语言来进行实现)

进一步说,你下达的命令是一个总的命令,而计算机执行时是讲该命令分为不同的阶段来执行的。举个简单的例子,当你打开计算机是,你只需按一下开机键,也就是下达你的Start命令。此时计算机收都命令后就会执行硬盘启动、点亮屏幕、加载系统等等一系列的操作,而这些操作都是你下达Start命令后其自动完成的。再比如,你使用Linux系统下的Shell时,你会在Shell中输入各种命令,然后计算机就会根据你的命令来执行系列的操作,具体哪些操作是对外隐藏的。这也就是对具体的实现细节进行了封装,并对外留出调用接口。

看完上面的描述,是时候回到今天的主题“命令模式”上了。命令模式简单的说就是将一些列的命令(函数或者方法)进行封装,隐藏内部执行细节,并对外留出调用的接口。命令模式是支持撤销操作的,撤销所做的事情就是与你刚才下达的命令相反。下方就是命令模式的定义了,说白了命令模式就是进一步对命令进行封装,简化命令的执行。这些命令在编程中就是一个个的函数。也可以说“命令模式”是对函数调用的封装,简化了函数调用的方式,隐藏了函数调用的细节。

命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

今天博客中会通过命令模式来实现控制台是示例,也就是我们可以通过给控制台下达不同的命令来进行不同的操作。

一、控制台命令模式的类图

下方就是我们将要实现的控制台“命令模式”的示例的类图。下方的类图还是比较简单的,红框上方是具体的类(计算机和电灯)。这些具体的类中有属于自己的不同的命令(对应着不同的函数函数),比如Computer类(计算机)中有Start、ScreenLight、Load、StartOver、Off等命令,而Light(电灯)类中有On、Off命令。

而红框中是我们示例的核心,也就是对命令的上述具体命令的封装。在封装命令时我们会实现一个接口(该示例中是Command协议),该接口就是外部执行命令( execute() 函数)的接口。封装的不同的命令都会遵循Command协议,所以我们封装的命令都会对外暴漏一个execute()函数,用来执行我们封装的命令。在封装命令时,我们会根据封装的命令的特点来执行特定的命令。比如下方的ComputerStartCommand命令中的就会包括Start、ScreenLight、Load、StartOver等子命令。也就是说在ComputerStartCommand命令中调用了Computer中的部分命令(函数),也就是对Computer类中的函数的调用做了进一步的封装,所以ComputerStartCommand命令要依赖于Computer类的。

红框下方就是我们的Console(控制台)类,Console是依赖于命令的接口而不依赖于命令的具体实现,这对模块间的解耦是非常有用的。在Console类中的command属性就是我们所依赖的命令接口,我们可以给command赋值不同的具体命令的实现,然后在Action函数中去执行具体的命令。请参加下方的类图。

设计模式(六):控制台中的“命令模式”(Command Pattern)

二、根据上述类图进行代码实现

上一部分对类图进了详细的介绍,接下来我们对上述类图进行代码实现就不是什么难事了。我们实现的步骤是与上面分析的步骤是一致的,自上而下。首先我们会给出Computer与Light的实现,然后是红框中的命令集合的实现,最后是Console的实现。如下所示:

1、具体命令执行对象的实现

首先我们要对真正去执行命令的对象进行代码实现,在该实例中就是Computer类和Light类。当然从上述示例中,我们不难看出这两个类要包括哪些命令(方法)。下方就是我们Computer类和Light类的具体实现,因为代码较为简单,在此就不做过多的赘述了。具体代码实现如下所示:

设计模式(六):控制台中的“命令模式”(Command Pattern)

2.对上述设备的命令进行封装

下方代码段就是上面类图中的红框部分的代码的具体实现。我们将Command声明为协议(命令对外的接口),而LightOnCommand、LightOffCommand、ComputerStartCommand具体的命令集合都遵循与这个协议。也就是说着三种不同的命令实现对外都有统一的接口,LightOnCommand与LightOffCommand都依赖于Light类,并且根据自己的命令种类来调用Light不同的方法,这也符合面向对象设计的单一职责。ComputerStartCommand命令类则依赖于Compute外设,会调用Computer中与启动相关的具体子命令。具体代码如下所示:

设计模式(六):控制台中的“命令模式”(Command Pattern)

3. 控制台的具体实现

上面我们实现了类图上面的两个模块,紧接着我们要实现类图最下方的那个类,也就是Console类。Console类也是比较简单的,Console类依赖于Command接口,其中的command存储属性就是用来存储那些遵循Command协议的类的对象的。在Console类中的action()方法是用来执行命令的。具体实现如下。

设计模式(六):控制台中的“命令模式”(Command Pattern)

三、测试用例

经过上面的类图的介绍与具体的代码实现,相比对“命令模式”有点概念了吧。最后一部分我们要对上述代码的实现进行测试,看一下我们的代码实现是否有问题。下方代码截图就是我们的测试用例以及该测试用例执行后输出的结果。首先我们创建了一个控制台的对象(类似于我们打开了一个Shell窗口),然后输出不同的命令(setCommand()),最后进行执行(调用action())。测试用例具体如下所示:

设计模式(六):控制台中的“命令模式”(Command Pattern)

至此我们的“命令模式”的一个完整示例就执行完了,最后用一句话来总结一下命令模式,那就是“命令模式是对一些列函数的调用的封装,然后留出执行的接口”。

同样今天的Demo也会在github上进行分享,分享地址为:https://github.com/lizelu/DesignPatterns-Swift