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

为方便读者,本文已添加至索引:html

写在前面

在上篇Chain of Responsibility(职责链)模式笔记中,咱们学习了一种行为型设计模式。今天,咱们继续这一主题,来学习下Command(命令)模式。能够看到职责链模式是对处理请求的对象(职能者)进行了建模,而Command模式的最大不一样之处就在于,它是对请求自己进行建模的。这一点从它的名字就能够看出。因此它又有别名叫:Action(动做)、Transaction(事物)模式。设计模式

老规矩,咱们首先直观地去理解什么是命令模式。平常工做中,咱们经常会接受到一些“任务”,这些任务每每伴随着具体要求,并对应着特定的执行人。好比说咱们作一个项目,我负责其中一个Feature。那么以后,上头提出的关于这个Feature的任何修改的“command”,都会让我去执行,由于我拥有开发这个Feature所必需的相关信息。app

实现这一模型的关键点就在于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模式使得传感器不须要知道任何驱动器的信息,这很是有助于咱们往后开发更多的驱动装置。spa

首先来看最重要的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是询问用户须要踢击的高度,必要的时候它得跳起来。code

 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 = askDirection();
20     int height = askHeight();
21     
22     if (direction && height > 0) {
23         _drive->kick(direction, height);
24     }
25 }

这样一来,咱们的传感器就能够经过接收并调用命令来使得小人偶动起来了:htm

1 // ... Sensor Process ...
2 ArmDrive* arm = ArmDrive::getInstance();
3 LegDrive* leg = LegDrive::getInstance();
4 Command* aCommand = new PunchCommand(arm);
5 aCommand->execute();    // execute command.
6 delete aCommand;
7 aCommand = new KickCommand(leg);
8 aCommand->execute();    // execute command.

Command模式的好处还不只限于此。你们必定熟悉批处理、宏等概念吧。结合Composite设计模式(请见索引),咱们能够设计MacroCommand,从而批量执行一系列的命令。而对于MarcroCommand自己来讲,它也无须要知道其各个子命令的具体执行者,由于它只需调用子命令便可。它具备add和remove方法来管理子命令。对象

 1 // ... Macro Command ...
 2 class MacroCommand : public Command {
 3 public:
 4     MacroCommand();
 5     virtual ~MacroCommand();
 6     
 7     virtual void add(Command*);  8     virtual void remove(Command*);  9     
10     virtual void execute(); 11 private:
12     List<Command*>* _cmds;
13 };
14 
15 void MacroCommand::execute() {
16     ListIterator<Command*> i (_cmds);
17     
18     for (i.first(); !i.isDone(); i.next()) {
19         Command* c = i.currentItem();
20         c->execute();
21     }
22 }
23 
24 void MacroCommand::add (Command* c) {
25     _cmds->append(c);
26 }
27 
28 void MacroCommand::remove(Command* c) {
29     _cmds->remove(c);
30 }

举例来看,咱们想让小人偶打出一系列拳脚组合的“连招”:

1 // ... Punch Punch  Kick and Punch ...
2 MacroCommand* aMCmd = new MacroCommand();
3 aMCmd->add(new PunchCommand(arm));
4 aMCmd->add(new PunchCommand(arm));
5 aMCmd->add(new KickCommand(leg));
6 aMCmd->add(new PunchCommand(arm));
7 aMCmd->execute();

怎么样,是否是很酷!让咱们来看一下这个设计的UML图:

特色总结

从以上咱们能够看到,Command模式具备以下一些特色:

  1. 它将调用操做的对象与知道如何实现该操做的对象解耦;
  2. Command是头等的对象。它们可像其余的对象同样被操纵和扩展;
  3. 可将多个命令装配成一个复合命令。好比MacroCommand;
  4. 增长新的Command很容易,由于这无需改变已有的类。

写在最后

今天的笔记就到这里了,欢迎你们批评指正!若是以为能够的话,好文推荐一下,我会很是感谢的!

相关文章
相关标签/搜索