命令模式
意图
将一个请求封装为一个对象,从而使你可以在接收不同的请求、请求队列或者日志请求时对客户进行参数化,并且支持可撤消的操作。
使用场景
当你有如下需求时,可使用Command模式:
- 可以抽象出待执行的动作以参数化某对象。
你可用过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command模式是回调机制的一个面向对象的替代品。 - 在不同的时刻指定、排列和执行请求。
- 支持取消操作。
- Command模式提供了对事务进行建模的方法。
结构
命令模式结构如下
Command
– 声明执行操作的接口。ConcreteCommand
– 将一个Receiver对象与一个Action绑定。
– 调用接收者相应的操作,以实现Execute。Client
– 创建一个Concrete Command对象并设定它的Receiver。Invoker
– 要求该命令执行这个请求。Receiver
– 知道如何实施与执行一个请求相关的操作。任何类都可能作为一个Receiver。
协作
- Client创建一个ConcreteCommand对象并指定它的Receiver对象。
- 某Invoker对象存储该ConcreteCommand对象。
- 该Invoker通过调用Command对象的Execute操作来提交一个请求。若该命令是可撤消的,ConcreteCommand就在执行Excute操作之前存储当前状态以用于取消该命令。
- ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。
时序图如下所示
效果
- Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
- Command是顶层的对象。它们可像其他的对象一样被操纵和扩展。
- 你可将多个命令装配成一个复合命令
- 扩展性良好,增加新的Command很容易,因为这无需改变已有的类。
命令模式实现(Implement)
案例
饮品店新开,有三种供客人选择,分别是奶茶、咖啡和果汁,现在用命令模式将这三种点单行为封装成对象。
代码实现
点单Order类,对应Command
柜台类BeverageCounter,对应Receiver
BeverageShopBroker类,对应Invoker
具体命令类,这里只展示BuyCoffee
Client类
这里的撤销命令并没有使用命令模式,只是将列表第一条匹配的订单对象撤销,如果想要使用撤销,必须在Command抽象中定义unexecute方法来取消执行,并且ConcreteCommand类中还需要储存额外的状态信息:
- Receiver对象,它真正执行处理该请求的各操作。
- Receiver上执行操作的参数。
- 如果处理请求的操作会改变Receiver对象中的某些值,那么这些值也必须先存储起来。Receiver还必须提供一些操作,以使该命令可将Receiver恢复到它先前的状态。
总结
命令模式的核心在于将请求或者动作(action)转换为事务,用对象来表示行为,使得行为可以重复操作或者取消。