行为模式——命令模式
1. 意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
2. 动机
有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接收者的任何信息。比如用户界面的工具箱,对于写工具箱源码的人而言,他们肯定不会知道这些工具箱内部的按钮和菜单究竟需要什么功能,也不可能知道。但是对于使用工具箱的人而言,却需要向这些工具箱编写相应的回调函数,以达成自己的目的,所以需要有一种方式,可以将工具箱底层代码和工具箱功能代码分离。命令模式解决的就是这么一类问题。
命令模式通过将请求本身变成一个对象来使工具箱可向未指定的应用对象提交请求。这个对象可被存储或者向其他对象传递。这一模式的关键就是一个抽象的Command类,它定义了一个执行操作的接口。最简单的形式就是提供一个Execute操作。
3. 适用性
- 抽象出待执行的动作及参数化某对象。就像前面的菜单功能一样。
- 在不同时刻指定,排列和执行请求。
- 支持取消操作。这需要和备忘录模式相结合。
- 支持修改日志,这样当系统崩溃的时候,这些修改可以被重做一遍。
- 用构建在原语操作上的高层操作构造一个系统。
4. 结构
5. 参与者
- Command
—— 声明执行操作的接口 - ConcreteCommand
—— 将一个接收者对象绑定于一个动作。
—— 调用接收者相应的操作,以实现Execute。 - Invoker
—— 要求该命令执行这个请求。 - Receiver
—— 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
6. 协作
- client创建一个ConcreteCommand对象并指定它的Receiver对象。
- 某Invoker对像存储该ConcreteCommand对象。
- 该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可以撤销的,ConcreteCommand就在执行Execute之前存储当前状态以用于取消该命令。
- ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。
其具体的时序图如下:
7. 效果
- Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
- Command模式是头等的对象。它们可像其他对象一样被操纵和扩展。
- 你可将多个命令装配成一个复合命令。
- 增加新的Command很容易,因为这无需改变已有的类。
8. 实现
- 一个命令对象应该达到何种智能程度。
- 支持取消和重做。如果Command提供方法逆转(reverse)它们的操作的执行,就可以支持取消和重做功能。为了达到这个目的,ConcreteCommand类可能需要存储额外的状态信息。这个状态可能包括:
· 接收者对象,它真正执行处理该请求的各种操作。
· 接收者上执行操作的参数。
· 如果处理请求的操作会改变接收者对象的某些值,那么这些值也必须保存起来。 - 避免取消操作过程中的错误积累。这里可以使用Memento模式来让该Command访问这些信息而不暴露其他对象的内部信息。
- 使用C++模板。对于(1)不能取消(2)不需要参数的命令,我们可使用C++模板来实现。
9. 代码示例
github地址:https://github.com/VioletDream-SXZ/DesignPatterns/tree/master/BehaviorPattern
该项目的UML图如下:
参考文献
Erich Gamma,Richard Helm,Ralph Johnson,John Vissides.Design Patterns Elements of Reusable Object-Oriented Software[M].北京:机械工业出版社.2009:9.