以下内容均来自GeekBand极客班C++ 设计模式课程(李建忠老师主讲)
Command
“行为变化”模式
在组件的构建过程中,组件行为的变化经常导致组件本身剧烈的变化。“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现两者之间的松耦合。
典型模式:
Command
Visitor
成员函数一般分为两类,虚函数和非虚函数(或静态),非虚函数(或静态)是编译的时候以地址的形式直接绑定,虚函数是运行时通过虚函数指针进行绑定
动机(Motivation)
在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。
但在某些场合——比如需要对行为进行“记录、撤销/重(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
《设计模式》GOF的定义:
将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作
红色部分为稳定部分,蓝色部分是变化部分
示例
现在有一个抽象的命令存在
class Command{
public:
virtual void execute() = 0;
};
我们下面有很多命令,将这些命令全部继承于抽象命令
class ConcreteCommand1 : public Command{
string arg;
public:
ConcreteCommand1(const string & a) : arg(a){}
void execute(){
cout<<"#1 process..."<<arg<<endl;
}
};
class ConcreteCommand2 : public Command{
string arg;
public:
ConcreteCommand1(const string & a) : arg(a){}
void execute(){
cout<<"#2 process..."<<arg<<endl;
}
};
可以穿件一个参数命令列
class MarcoCommand : public Command{
vector<Command *> commands;
public:
void addCommand(Command * c){ commands.push_back(c); }
void execute(){
for(auto item:commands){
item->execute();
}
}
};
具体的操作如下:创建命令,并添加到命令列,然后进行执行
各式命令已经变成了具体的对象,可以针对这些对象进行操作
ConcreteCommand1 command1(receiver,"Arg ###");//命令1
ConcreteCommand1 command2(receiver,"Arg $$$");//,命令2
MarcoCommand macro;//构成一个命令列
macro.addCommand(&command1);
macro.addCommand(&command2);
macro.execute();//执行命令
要点总结
Command模式的根本目的在于将“行为请求者”与:行为实现者解耦,在面对对象语言中,常见的实现手段是“将行为抽象为对象”
实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息。通过使用Composite模式,可以将多个“命令”封装为一个“复合命令”MacroCommand
Command模式与C++中的函数对象有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口——实现”来定义行为接口规范,更严格,但性能损失(毕竟是运行时绑定)
C++函数对象以函数签名来定义行为接口规范,更灵活,性能更高。