命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
模式动机
在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。
命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。
模式分析
命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
主要解决:
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:
在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
应用实例:
struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。(待了解)
优点:
- 降低了系统耦合度。
- 新的命令可以很容易添加到系统中去。
- 允许接收请求的一方决定是否要否决请求。
- 能较容易地设计一个命令队列。
- 可以容易地实现对请求的撤销和恢复。
- 在需要的情况下,可以较容易地将命令记入日志。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景
认为是命令的地方都可以使用命令模式,比如:
- GUI 中每一个按钮都是一条命令。
- 模拟 CMD。
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。
模式结构
命令模式包含如下角色:
- Command: 抽象命令类
- ConcreteCommand: 具体命令类
- Invoker: 调用者
- Receiver: 接收者
- Client:客户类
最近了解了一个工具,Graphviz:
使用它可以生成UML图:
这个布局有点丑,下次再改改:
digraph {
node [shape=Mrecord, fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
Client [label="{
Client
}"]
Invoker [label="{
Invoker
|
- command : Command\l
|
+ call() : void\l
}"]
Command [label="{
Command
|
+ execute() : void\l
}"]
ConcreteCommand [label="{
ConcreteCommand
|
- receiver : Receiver\l
|
+ execute() : void\l
}"]
Receiver [label="{
Receiver
|
+ action() : void\l
}"]
{
Client -> { Invoker, Receiver } [arrowhead=vee, style=dashed]
Invoker -> Command [dir=back, arrowtail=odiamond]
ConcreteCommand -> Command [arrowtail=onormal, style=dashed]
ConcreteCommand -> Receiver [arrowhead=vee]
}
}
代码
#include <iostream>
namespace command_pattern {
class command {
public:
virtual void execute() = 0;
};// class command
class receiver {
public:
void action() {
std::cout << "receiver action" << std::endl;
}
};// class receiver
class concrete_command : public command {
public:
concrete_command(receiver *receiver_arg) {
this->receiver_ = receiver_arg;
}
void execute() override {
std::cout << "concrete_command execute " << std::endl;
receiver_->action();
}
private:
receiver *receiver_;
};// class concrete_command
class invoker {
public:
invoker(command *command_arg) {
this->command_ = command_arg;
}
void call() {
std::cout << "invoker call " << std::endl;
command_->execute();
}
private:
command *command_;
}; // class invoker
} // namespace command_pattern
int main() {
command_pattern::receiver _receiver;
command_pattern::concrete_command _concrete_command(&_receiver);
command_pattern::invoker _invoker(&_concrete_command);
_invoker.call();
return 0;
}
执行结果为:
invoker call
concrete_command execute
receiver action
总结
学的有点懵。。