跟着《重构》学设计模式——命令Command模式

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

分类

对象行为型模式

意图

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

动机

有时必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息。命令模式通过将请求本身变成一个对象来使工具箱对象可向未指定的应用对象提出请求。这个对象可以被存储并像其他的对象一样被传递。

适用性

  1. 抽象出待执行的动作以参数化某对象时:command模式是回调机制的一个面向对象的替代品。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。
  2. 在不同的时刻指定、排列和执行请求。
  3. 支持取消操作。Command的Execute操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute和Execute来实现重数不限的“取消”和“重做”。
  4. 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
  5. 用构建在原语操作上的高层操作构造一个系统。这样的一种结构在支持事务的信息系统中很常见。

UML

2024-03-20-11-26-32-20240320112631

调用关系

2024-03-20-11-26-47-20240320112646

效果

  1. 将调用操作的对象与知道如何实现该操作的对象解耦
  2. Command对象可像其他对象一样被操纵与扩展
  3. 可将多个命令装配成一个复合命令
  4. 增加新的Command很容易,因为这无需改变已有的类

实现

实现Command模式时需要考虑以下问题:

  1. 一个命令对象应达到何种智能程度:命令对象的能力可大可小,一个极端是它仅确定一个接收者和执行该请求的动作,另一个极端是它自己实现所有功能,根本不需要额外的接收者对象。当需要定义与已有的类无关的命令,当没有合适的接收者,或当一个命令隐式地知道它的接收者,可以使用后一极端方式。
  2. 支持取消(undo)和重做(redo):如果Command提供方法逆转它们操作的执行,就可支持取消和重做。为达到这个目的,ConcreteCommand类可能需要存储额外的状态信息。这个状态包括:
    1. 接收者对象,它真正执行处理该请求的各操作;
    2. 接收者上执行操作的参数
    3. 如果处理请求的操作会改变接收者对象中的某些值,那么这些值也必须先存储起来,接收者还必须提供一些操作,以使该命令可将接收者恢复到它先前的状态。
  3. 避免取消操作过程中的错误积累
  4. 使用C++模板