创建型模式(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模式的典型结构图为:
图 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
类的引用,但是其作用也仅仅就是为了实现这个Command
的Excute
接口)。这种方式在是纯正的面向对象设计者最为鄙视的设计方式,就像OO设计新手做系统设计的时候,仅仅将Class
作为一个关键字,将C种的全局函数找一个类封装起来就以为是完成了面向对象的设计。
但是世界上的事情不是绝对的,上面提到的方式在OO设计种绝大部分的时候可能是一个不成熟的体现,但是在Command模式中却是起到了很好的效果。主要体现在:
- Command模式将调用操作的对象和知道如何实现该操作的对象解耦。在上面Command的结构图中,
Invoker
对象根本就不知道具体的是那个对象在处理Excute
操作(当然要知道是Command
类别的对象,也仅此而已)。
- 在
Command
要增加新的处理操作对象很容易,我们可以通过创建新的继承自Command
的子类来实现这一点。
- Command模式可以和Memento模式结合起来,支持取消的操作。