[学习笔记]设计模式之Command(一)

2014-11-24 00:35:09 · 作者: · 浏览: 0
写在前面
在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式。今天,我们继续这一主题,来学习下Command(命令)模式。可以看到职责链模式是对处理请求的对象(职能者)进行了建模,而Command模式的最大不同之处就在于,它是对请求本身进行建模的。这一点从它的名字就可以看出。所以它又有别名叫:Action(动作)、Transaction(事物)模式。
老规矩,我们首先直观地去理解什么是命令模式。日常工作中,我们常常会接受到一些“任务”,这些任务往往伴随着具体要求,并对应着特定的执行人。比如说我们做一个项目,我负责其中一个Feature。那么之后,上头提出的关于这个Feature的任何修改的“command”,都会让我去执行,因为我拥有开发这个Feature所必需的相关信息。
实现这一模型的关键点就在于Command的抽象类,它定义了一个执行操作的接口,比如说execute()操作。具体的一个Command子类将接收者作为它的一个实例变量保存,并实现execute()操作,指定接收者行动起来。我们将在示例部分进行更深入地实例讲解,那么在此之前我们可以了解下它的基本要点:
要点梳理
目的分类
对象行为型模式
范围准则
对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
主要功能
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作
适用情况
当我们需要抽象出待执行的动作以参数化某对象时。
在不同的时刻指定、排列和执行请求。
需要支持取消操作。
需要支持修改日志,这样当 系统崩溃时,这些修改可以被重做一遍。
用构建在原语操作上的高层操作构造一个系统。
参与部分
Command:声明执行操作的接口
ConcreteCommand:将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现execute
Client:创建一个具体命令对象并设定它的接收者
Invoke:要求该命令执行这个请求
Receiver:知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者
协作过程
Client创建一个ConcreteCommand对象并指定它的Reciever对象
某个Invoker对象存储ConcreteCommand对象
该Invoker通过调用Command对象的execute操作来提交一个请求。若该命令是可以取消的,那么ConcreteCommand就在执行execute之前存储当前状态以用于撤销
ConcreteCommand对象对调用它的Receiver的一些操作执行该请求
UML图
示例分析 - 魔法小人偶的驱动器
让我们回顾下女巫格琳达的私人订制魔法小人偶吧(详见Composite模式笔记)。它通过某种神秘的传感器作为Input装置,接收人们下达的命令,然后执行。为了简单起见,我们今天举一个具体的“武斗人偶”的例子。这种人偶俨然一位空手格斗大师,为了灵活地展现武斗技巧,它分别有对应于手臂和腿部的驱动装置(ArmDrive & LegDrive),人们可以给它下达诸如“挥击”(Punch)、“飞踢”(FlyingKick) 等命令。为了增强人偶功能的扩展性,我们当然希望它日后可以执行更多更多的命令。Command设计模式的引入帮我们很好地解决了需求。它使得调用操作的对象与知道如何实现该操作的对象解耦。我们的例子中,人偶的传感器就是调用操作的对象,而各种驱动器则是实现具体操作的对象,Command模式使得传感器不需要知道任何驱动器的信息,这非常有助于我们日后开发更多的驱动装置。
首先来看最重要的Command类:
复制代码
1 // ... Direction definitions ...
2 #define NONE 0
3 #define D_SOUTH 1
4 #define D_NORTH 2
5 #define D_WEST 3
6 #define D_EAST 4
7
8 // ... Abstract Command class ...
9 class Command {
10 public:
11 virtual ~Command();
12 virtual void execute() = 0;
13 protected:
14 Command();
15 };
复制代码
接下来是PunchCommand,它会令ArmDrive对象朝用户指定的方向来狠狠重击。注意,PunchCommand的构造器需要一个ArmDrive对象作为参数。askDirection是提示用户告诉它具体的方向。
复制代码
1 // Punch !
2 class PunchCommand : public Command {
3 public:
4 PunchCommand(ArmDrive*);
5
6 virtual void execute();
7 protected:
8 virtual int askDirection();
9 private:
10 ArmDrive* _drive;
11 };
12
13 PunchCommand::PunchCommand(ArmDrive* a) {
14 _drive = a;
15 }
16
17 void PunchCommand::execute() {
18 int direction = askDirection();
19
20 if (direction) {
21 _drive->punch(direction);
22 }
23 }
复制代码
同理我们有KickCommand需要一个LegDrive对象作为其接收者。其中askHeight是询问用户需要踢击的高度,必要的时候它得跳起来。
复制代码
1 // Kick !
2 class KickCommand : public Command {
3 public:
4 KickCommand(LegDrive*);
5
6 virtual void execute();
7 protected:
8 virtual int askHeight();
9 virtual int askDirection();
10 private:
11 LegDrive* _drive;
12 };
13
14 KickCommand::PunchCommand(LegDrive* l) {
15 _drive = l;
16 }
17
18 void KickCommand::execute() {
19 int direction = askDire