command模式
参考资料
• 维基百科:https://en.wikipedia.org/wiki/Command_pattern
Command模式简介
GoF:Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
GoF:将一个请求封装成一个对象,从而让用户可以用不同的请求对客户程序进行参数化,同时可以使请求形成请求队列、记录请求日志,以及支持可撤销的操作。
Wikipedia:In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.
Four terms always associated with the command pattern are command, receiver, invoker and client. A command object knows about receiver and invokes a method of the receiver. Values for parameters of the receiver method are stored in the command. The receiver then does the work. An invoker object knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker does not know anything about a concrete command, it knows only about command interface. Both an invoker object and several command objects are held by a client object. The client decides which commands to execute at which points. To execute a command, it passes the command object to the invoker object.
Using command objects makes it easier to construct general components that need to delegate, sequence or execute method calls at a time of their choosing without the need to know the class of the method or the method parameters. Using an invoker object allows bookkeeping about command executions to be conveniently performed, as well as implementing different modes for commands, which are managed by the invoker object, without the need for the client to be aware of the existence of bookkeeping or modes.
Wikipedia:在面向对象编程中,命令模式是一种行为设计模式。在这种模式中,一个命令对象被用来封装执行一个动作或触发一个事件的所有信息,这些信息包括函数名和拥有该函数及函数参数的对象。
四个与命令模式密切相关的术语是:command(命令)、receiver(接受者)、invoker(调用者)和client(客户端)。一个command对象知道receiver信息,并将调用receiver的一个方法。这个receiver方法的参数将被存储在command对象中,之后receiver将进行实际处理。一个invoker对象知道如何执行一个command,并且可以选择性地对command执行过程进行日志记录。invoker对象对于一个具体的command的细节是一无所知的,它所掌握的信息只是command提供的接口。invoker对象和command对象将被client对象持有,client对象将决定一个command执行的时机。为了执行一个command,我们将传递command对象实体给invoker对象实体使用。
使用命令对象能够更加容易地构建通用组件。这些通用组件通常需要委托、序列化或执行函数调用,而不需要知道函数所属的类或函数参数。使用一个invoker对象,可以使记录命令执行日志或实现命令的不同模式能够更加方便地进行,此时client并不需要知道记录日志或命令模式的存在。
百度百科:在面向对象程式设计的范畴中,命令模式尝试以物件来代表实际行动。在软件系统中,行为请求者与行为实现者通常呈现一种紧耦合关系。将行为请求者与行为实现者解耦,将一组行为抽象为对象,实现二者之间的松耦合,这就是命令模式。
Command模式详解
• 设计意图
将一个请求封装成一个对象,从而让用户可以用不同的请求对客户程序进行参数化,同时可以使请求形成请求队列、记录请求日志,以及支持可撤销的操作。用于行为请求者与行为实现者之间的解耦,分离变化与不变的因素,以便适应变化。
• UML类图
• 结构说明
▶ Command
GoF:Declares an interface for executing an operation.
GoF:为后续执行的操作声明了接口。
▶ ConcreteCommand
GoF:Defines a binding between a Receiver object and an action. Implements Execute by invoking the corresponding operation(s) on Receiver.
GoF:在一个receiver对象和一个action之间定义一个捆绑。通过在receiver上调用相应的操作实现了execute的过程。
▶ Client
GoF:Creates a concrete Command object and sets its Receiver.
GoF:创建一个具体的command对象并设置其Receiver对象。
▶ Invoker
GoF:Asks the command to carry out the request.
GoF:要求command来执行request。
▶ Receiver
GoF:Knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.
GoF:知道如何执行与执行一个request相关的操作。任何一个类型都可能作为receiver提供服务。
• 模式特点
• 每个具体command对象持有一个对具体执行者receiver对象的引用。
• invoker对象中持有所有具体command对象的引用。
• 当一个request到达时,invoker对象调用对应的command对象执行(execute),command对象内部将调用其持有的具体receiver对象执行真正的处理操作。
• command模式将硬编码中的switch-case语句去除,当新增一个新的request时,只需要找到其对应的receiver,编写一个新的command类型,并将其注册给invoker即可。
• 使用command模式可能会产生过多的具体命令类,因此要针对实际情况以及将来系统的扩展情况来合理使用。
Decorator模式举例
• 例子一
这个例子出自于Wikipedia:https://en.wikipedia.org/wiki/Command_pattern
以一个简单开关为例,我们使用两种命令来配置这个开关:开灯和关灯。
这里,基于命令模式实现的一个好处就是,这个开关可以被任何设备使用,而不仅仅只能被灯使用:下面例子中,Switch类被用于开灯和关灯,但是Switch类构造器可以接受Command类的任何子类做为参数,因此,你可以配置Switch类来用于开关一个引擎。
关于一个开关如何从最原始设计衍化为基于命令模式设计的过程,可以参见这篇《Command 模式 Step by Step》文章。
#include <stdio.h> #include <iostream> using namespace std; // The Command interface : ICommand class ICommand { public: ICommand() {} virtual ~ICommand() {} public: virtual void Execute() = 0; }; // The Invoker entity : Switch class Switch { private: ICommand *m_pClosedCommand; ICommand *m_pOpenedCommand; public: Switch(ICommand *pClosedCommand, ICommand *pOpenedCommand) { m_pClosedCommand = pClosedCommand; m_pOpenedCommand = pOpenedCommand; } ~Switch() { if ( m_pClosedCommand ) { delete m_pClosedCommand; m_pClosedCommand = NULL; } if ( m_pOpenedCommand ) { delete m_pOpenedCommand; m_pOpenedCommand = NULL; } } public: void Close() { if ( m_pClosedCommand ) { m_pClosedCommand->Execute(); } } void Open() { if ( m_pOpenedCommand ) { m_pOpenedCommand->Execute(); } } }; // The Receiver interface : ISwitchable (sometimes unnecessary) class ISwitchable { public: ISwitchable() {} virtual ~ISwitchable() {} public: virtual void PowerOn () = 0; virtual void PowerOff() = 0; }; // The Receiver entity : Light class Light : public ISwitchable { public: Light() {} virtual ~Light() {} public: virtual void PowerOn() { cout << "The light is on." << endl; } virtual void PowerOff() { cout << "The light is off." << endl; } }; // The Concrete Command : CloseSwitchCommand class CloseSwitchCommand : public ICommand { private: ISwitchable *m_pSwitchable; public: CloseSwitchCommand(ISwitchable *pSwitchable) { m_pSwitchable = pSwitchable; } virtual ~CloseSwitchCommand() { m_pSwitchable = NULL; } public: virtual void Execute() { if ( m_pSwitchable ) { m_pSwitchable->PowerOff(); } } }; // The Concrete Command : OpenSwitchCommand class OpenSwitchCommand : public ICommand { private: ISwitchable *m_pSwitchable; public: OpenSwitchCommand(ISwitchable *pSwitchable) { m_pSwitchable = pSwitchable; } virtual ~OpenSwitchCommand() { m_pSwitchable = NULL; } public: virtual void Execute() { if (m_pSwitchable) { m_pSwitchable->PowerOn(); } } }; int main() { // light receiver ISwitchable *pLamp = ::new Light; // concrete command ICommand *pClosedCommand = ::new CloseSwitchCommand(pLamp); ICommand *pOpenedCommand = ::new OpenSwitchCommand (pLamp); // invoker Switch *pSwitch = ::new Switch(pClosedCommand, pOpenedCommand); char c; for (;;) { ::scanf("%c", &c); if ('o' == c) { pSwitch->Open(); continue; } if ('c' == c) { pSwitch->Close(); continue; } if ('q' == c) { break; } } delete pSwitch; delete pLamp; return 1; }