创建型模式(Creating Pattern)

Factory 模式 | AbstactFactory 模式 | Singleton 模式 | Builder 模式 | Prototype 模式

结构型模式(Structrual Pattern)

Bridge 模式 | Adapter 模式 | Decorator 模式 | Composite 模式 | Flyweight 模式 | Facade 模式 | Proxy 模式

行为型模式(Behavioral Pattern)

Template 模式 | Strategy 模式 | State 模式 | Observer 模式 | Memento 模式 | Mediator 模式 | Command 模式 | Visitor 模式 | Iterator 模式 | Interpreter 模式 | Chain of Responsibility 模式

Command 模式简介:

   命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
   在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

问题:

   Command模式通过将请求封装到一个对象(Command)中,并将请求的接受者存放到具体的ConcreteCommand类中(Receiver)中,从而实现调用操作的对象和操作的具体实现者之间的解耦。

模式选择

   Command模式的典型结构图为:
Command Pattern
图 1:Command 模式结构示意图

   Command模式结构图中,将请求的接收者(处理者)放到Command的具体子类ConcreteCommand中,当请求到来时(Invoker发出Invoke消息激活Command对象),ConcreteCommand将处理请求交给Receiver对象进行处理。

实现

完整代码示例(code)

Command模式的实现很简单,这里为了方便初学者的学习和参考,将给出完整的实现代码(所有代码采用 C++实现,并在 Visual Studio Code,Version: 1.36.1 下测试运行)。

源码gitee地址:点击这里

代码目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\Command> tree /F     
卷 Windows 的文件夹 PATH 列表
卷序列号为 F0C5-AFA6
C:.
├─include
│ Command.h
│ Concrete.h
│ Invoker.h
│ Receiver.h

└─src
a.out
Command.cpp
Concrete.cpp
Invoker.cpp
main.cpp
Reciever.cpp

PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\Command>

Command.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Command.h

#ifndef _COMMAND_H_
#define _COMMAND_H_

#include "Receiver.h"

class Command
{
public:
Command();
virtual ~Command();
virtual void execute() = 0;
};

#endif //_COMMAND_H_

Concrete.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Concrete.h

#ifndef _CONCRETE_H_
#define _CONCRETE_H_

#include "Command.h"
#include "Receiver.h"

class ConcreteCommand : public Command {
public:
ConcreteCommand(Receiver *pReceiver);
virtual ~ConcreteCommand();
virtual void execute();

private:
Receiver *m_pReceiver;
};

#endif //_CONCRETE_H_

Invoker.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Invoker.h

#ifndef _INVOKER_H_
#define _INVOKER_H_

#include "Command.h"

class Invoker {
public:
Invoker(Command *pCommand);
virtual ~Invoker();
void call();

private:
Command *m_pCommand;
};

#endif //_INVOKER_H_

Receiver.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Receiver.h

#ifndef _RECEIEVER_H_
#define _RECEIEVER_H_

class Receiver
{
public:
Receiver();
virtual ~Receiver();

void action();
};

#endif //~_RECEIEVER_H_

Command.cpp:

1
2
3
4
5
6
7
// Command.cpp

#include "../include/Command.h"
#include <iostream>

Command::Command(){};
Command::~Command(){};

Concrete.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
// Concrete.cpp

#include "../include/Concrete.h"
#include <iostream>
using namespace std;

ConcreteCommand::ConcreteCommand(Receiver *pReceiver) { m_pReceiver = pReceiver; }
ConcreteCommand::~ConcreteCommand() {}
void ConcreteCommand::execute() {
cout << "ConcreteCommand::execute" << endl;
m_pReceiver->action();
}

Invoker.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Invoker.cpp

#include "../include/Invoker.h"
#include <iostream>
using namespace std;

Invoker::Invoker(Command* pCommand) { m_pCommand = pCommand; }

Invoker::~Invoker() {}

void Invoker::call() {
cout << "invoker calling" << endl;
m_pCommand->execute();
}

Reciever.cpp:

1
2
3
4
5
6
7
8
9
10
// Receiver.cpp

#include "../include/Receiver.h"
#include <iostream>

Receiver::Receiver(/* args */) {}
Receiver::~Receiver() {}
void Receiver::action(){
printf("Receiver::Action...... \n");
}

main.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// main.cpp

#include <iostream>
#include "../include/Command.h"
#include "../include/Concrete.h"
#include "../include/Invoker.h"
#include "../include/Receiver.h"

using namespace std;

int main(int argc, char *argv[]) {
Receiver *pReceiver = new Receiver();
ConcreteCommand *pCommand = new ConcreteCommand(pReceiver);
Invoker *pInvoker = new Invoker(pCommand);
pInvoker->call();

if (NULL != pReceiver) delete pReceiver;
if (NULL != pCommand) delete pCommand;
if (NULL != pInvoker) delete pInvoker;

return 0;
}

代码说明

   Command模式在实现的实现和思想都很简单,其关键就是将一个请求封装到一个类中(Command),再提供处理对象(Receiver),最后Command命令由Invoker激活。另外,我们可以将请求接收者的处理抽象出来作为参数传给Command对象,实际也就是回调的机制(Callback)来实现这一点,也就是说将处理操作方法地址(在对象内部)通过参数传递给Command对象,Command对象在适当的时候(Invoke激活的时候)再调用该函数。这里就要用到C++中的类成员函数指针的概念。

编译运行结果:

1
2
3
4
5
PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\Command\src> .\a.exe 
invoker calling
ConcreteCommand::execute
Receiver::Action......
PS C:\Users\guopz\Desktop\GiteeBlog\designpatternsbycpipei\behavioralPattern\Command\src>

讨论

   Command模式的思想非常简单,但是Command模式也十分常见,并且威力不小。实际上,Command模式关键就是提供一个抽象的Command类,并将执行操作封装到Command类接口中,Command类中一般就是只是一些接口的集合,并不包含任何的数据属性(当然在示例代码中,我们的Command类有一个处理操作的Receiver类的引用,但是其作用也仅仅就是为了实现这个CommandExcute接口)。这种方式在是纯正的面向对象设计者最为鄙视的设计方式,就像OO设计新手做系统设计的时候,仅仅将Class作为一个关键字,将C种的全局函数找一个类封装起来就以为是完成了面向对象的设计。
   但是世界上的事情不是绝对的,上面提到的方式在OO设计种绝大部分的时候可能是一个不成熟的体现,但是在Command模式中却是起到了很好的效果。主要体现在:

  • Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图中,Invoker对象根本就不知道具体的是那个对象在处理Excute操作(当然要知道是Command类别的对象,也仅此而已)。
  • Command要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command的子类来实现这一点。
  • Command模式可以和Memento模式结合起来,支持取消的操作。