现在的位置: 首页 > 综合 > 正文

[设计模式笔记]三. 行为型模式–16. Command模式(命令模式)(一)

2013年02月05日 ⁄ 综合 ⁄ 共 3174字 ⁄ 字号 评论关闭

行为型模式--Command模式(命令)对象行为型模式

一. 意图

    将一个请求封装为一个对象从而使你可用不同的请求对客户进行参数化对请求排队或记录请求日志以及支持可撤消的操作.(封装成对象从而可以利用面向对象中继承多态重载等特性从而使得你的设计更灵活独立解耦内聚)


二. 适用性

          这一模式的关键是一个抽象的Command它定义了一个执行操作的接口其最简单的形式是一个抽象的Execute操作具体的Command子类将接收者作为其一个实例变量并实现Execute操作指定接收者采取的动作而接收者有执行该请求所需的具体信息.

当你有如下需求时可使用Command模式:

1. MenuItem对象那样抽象出待执行的动作以参数化某对象你可用过程语言中的回调(callback)函数表达这种参数化机制所谓回调函数是指函数先在某处

注册而它将在稍后某个需要的时候被调用. Command模式是回调机制的一个面向对象的替代品.

2. 在不同的时刻指定、排列和执行请求一个Command对象可以有一个与初始请求无关的生存期如果一个请求的接收者可用一种与地址空间无关的方式表达那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求.

3. 支持取消操作. CommandExcute操作可在实施操作前将状态存储起来在取消操作时这个状态用来消除该操作的影响. Command接口必须添加一个Unexecute操作该操作取消上一次Execute调用的效果执行的命令被存储在一个历史列表中可通过向后和向前遍历这一列表并分别调用UnexecuteExecute来实现重数不限的“取消”和“重做”.


4. 支持修改日志这样当系统崩溃时这些修改可以被重做一遍Command接口中添加装载操作和存储操作可以用来保持变动的一个一致的修改日志从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们


5. 用构建在原语操作上的高层操作构造一个系统这样一种结构在支持事务(transaction)的信息系统中很常见一个事务封装了对数据的一组变动. Command模式提供了对事务进行建模的方法. Command有一个公共的接口使得你可以用同一种方式调用所有的事务同时使用该模式也易于添加新事务以扩展系统.

三. 模式结构

图1 

InvokerReceiver并不知道对方解耦了


四. 角色说明

Command

—声明执行操作的接口.

ConcreteCommand

—将一个接收者对象绑定于一个动作.

—调用接收者相应的操作以实现Execute.

Client

—创建一个具体命令对象并设定它的接收者。

Invoker

—要求该命令执行这个请求.

Receiver

—知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者.

五. 执行

图2

    Client创建一个ConcreteCommand对象并指定它的Receiver对象Invoker对象存储该ConcreteCommand对象Invoker通过调用Command对象的Execute操作来提交一个请求若该命令是可撤消的, ConcreteCommand就在执行Excute操作之前存储当前状态以用于取消该命令.

ConcreteCommand对象对调用它的Receiver的一些操作以执行该请求。

// aClient

// 1. 创建关联ReceiverCommand的关联

pReceiver = new Receiver();

pCommand = new Command(pReceiver);

// 2. 创建InvokerCommand的关联.

// 例如把pCommand传递给Invoker, 反正Invoker知道对象pCommand

// 3. Invoker触发(调用)Command执行

pCommand->Execute();

说明


1. Command模式将调用操作的对象与知道如何实现该操作的对象解耦(将调用者和接收者解耦)

2. Command是头等的对象它们可像其他的对象一样被操纵和扩展.

3. 你可将多个命令装配成一个复合命令一般说来复合命令是Composite模式的一个实例.

4. 增加新的Command很容易因为这无需改变已有的类.

5. 一个命令对象应达到何种智能程度 命令对象的能力可大可小可以只是简单的调用aReceiver->Action(), 也可以自己独立实现aReceiverAction()(个人认为只要达到调用者和接收者解耦就好, Invoker一定通过调用Command::Execute()来完成操作即可).

6. 支持后退和前进(撤销和向前). 为达到这个目的, ConcreteCommand类可能需要存储额外的状态信息. (个人认为支持撤销操作这种功能可能是比较适合使用这种模式来实现但是关键还是看你怎么保存和记录状态信息使得能支持撤销操作.)

我的理解

1. Command模式目的是将调用者和接收者解耦自己开始的理解是Receiver中包含一个Command对象这样子很明显Invoker是通过Receiver来调用Command::Execute. ReceiverReceiver是不能解耦的.

2. 对于撤销功能当执行一条命令之前你需要保存当前的状态并放入历史列表

a. 可以这样子实现定义Command有记录执行之前的状态的能力有一个撤销函数用于恢复状态(UnDo()).

b. Command执行之前使用Prototype模式保存该Command到操作历史列表执行该Command

c. 如果要撤销就从历史列表取出最后入队的Command对象调用UnDo()函数.

要注意的还是怎么保存这些状态怎么恢复这些状态这些状态对全局的状态有什么影响

3. 菜单栏的菜单项使用这种模式你点击菜单项时执行的过程就是Command::Execute() ; Command::Execute()里面调用Receiver::Action()菜单项并不需要知道Receiver的具体细节在程序的某个地方有代码把 CommandReceiver绑定在一起也有代码把菜单项与Command绑定在一起(菜单项与Receiver直接隔着Command, 从而解耦).


4. 能这样子吗?

图3

具体的Receiver与具体的Command解耦了这样子也可以如果满足你的需求的话起码就是Receiver有公共的接口.

而在图1中子Command与对应的Receiver是必须关联的这也是正确的反正就是看你的需求而定而图3中则可以在运行时来决定Receiver, 而图1是一个子Command则固定的对应了其Receiver. 通常如果各个Receiver之间没有能提取出公共接口CommandReceiver一一对应也是很正常的.

5. 从点4可以看到Command模式的关键CommandReceiver的关联和InvokerCommand的内聚(Command隔开了InvokerReceiver)

相关模式

Composite模式可被用来实现宏命令. Memento模式可用来保持某个状态命令用这一状态来取消它的效果在被放入历史表列前必须被拷贝的命令起到一种原型(Prototype模式)的作用.

抱歉!评论已关闭.