设计模式:Command模式

Commad模式是一种对象行为模式, 它可以对发送者(sender)和接收者(receiver)完全解耦(decoupling)。("发送者" 是请求操作的对象,"接收者" 是接收请求并执行某操作的对象。有了 "解耦",发送者对接收者的接口一无所知。)

简单说明


 

简单来看,在对象A调用了对象B的一个方法,这样对象A就可以看做一个发送者,对象B就是一个接收者,在这种情况下,对象A必须知道对象B相应的方法名。如果在这里使用Command模式的话,就相当于在对象A和对象B之间加了一个中间层,由中间层来完成这一次的调用,在这种情况下,对象A并不需要知道对象B的方法。

Encapsulate a request as an object , thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

示例


 

Command模式的核心思想就是把一个请求包装为一个对象,由这个对象完成本次请求的调用。一个请求队列,或者说一个操作的do和undo都是可以很好应用Command模式的例子。

比如说,我们的操作系统里面设置了一些自启动的程序。也就是说,在系统启动的时候会一一启动这些程序。而这些程序的启动方法可能是不一样的,并且自启动程序的个数也是不定的。那么我们就可以把这些自启动程序的启动请求包装为Command对象,操作系统只需要知道这些Command对象就行了,而不需要知道那些自启动程序的详细信息。

又比如说,某老板谈妥了一笔生意。这个时候,他需要他的秘书准备合同,财务准备资金,人事招聘相关人才。假设我们有三个渠道分别对应了这三种操作,老板只要对这三个渠道下达执行命令即可,而不需要分别告诉他的秘书要准备合同,让财务准备资金,还得通知人事进行人才招聘。在这里,这三个渠道就是三个包装好的Command对象。

也许你觉得在上面这个需求里面,包装三个渠道是完全没有必要的,直接调用不就好了吗?确实是这样,假如每笔生意都是这样的操作流程的话。假如现在这个老板谈妥了另外一笔生意,而这笔生意呢,只需要准备合同和资金,并不需要招聘人才,因为现有人才是足够的。在这样的情况下,Command模式还是非常适用的,老板只要对秘书和财务的渠道下达执行命令就可以了。

再比如说,最常见的do/undo操作。假如我们要实现一个文本编辑器,并且要提供限定次数内的undo功能,此时Command模式可能算是一个再好不过的解决方案了。只要我们把用户每次的操作都包装为Command对象,并且总是保留最后的那一部分Command。那么当用户需要undo功能的时候,我们只需要从最后开始提取每一个Command对象,并且执行相应的undo方法就能实现了。

设计图


 

图一
Command Design Pattern

图一中这种设计,可能算是一种比较简单的Command模式了吧。Command只有一个execute()方法,而Invoker也只持有一个Command对象,也就是说,只能执行一种操作。

图二
Command Design Pattern

图二的设计糅合了一些功能。在这里,我们的Command对象不但有execute()方法,而且有undo()方法,这样就可以完成undo操作了。而且,Command的子类呢,既有接收某个Receiver对象的子类类型,也有接收一组Command对象的子类类型。这个时候的Invoker也已经有了执行一组Command对象的能力,也有undo一组Command的能力了。

图一和图二中,已经用颜色进行了区分。绿色的Command和Invoker是Command模式的核心,而黄色的Receiver对象暂时只是ConcreateCommand1包装的接受者(当然其他的Command子类的对象也可以使用),并不是必须存在的。

图中的依赖关系和关联关系并不是一定存在的。因为现在有类似于Spring这种IoC容器的存在,我们基本上可以实现两者之间的完全解耦。当然,对于Interface的依赖和关联可能还是存在的。

图中的代码只是示例代码,为了方便理解而已。

优点


 

★. 实现了命令的发送者和接收者之间的解耦
★. 很容易构造一个命令队列,也可以构建组合命令
★. 记录相关的命令日志
★. 增加命令的状态,实现命令的撤销和重做
★. 允许接受请求的一方决定是否可做
★. 新的命令轻而易举可以加入其中

缺点


 

☆. 可能会有过多的具体命令类存在

适用


 

★. 抽象出待执行的动作以参数化某对象。你可用过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command模式是回调机制的一个面向对象的替代品。在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程 并在那儿实现该请求。

 

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

 

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

 

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

参考资料


 

1. http://blog.csdn.net/chenshu181/archive/2005/06/03/387298.aspx
2. http://scanfprintf123.javaeye.com/blog/523253
3. http://dev.csdn.net/article/78143.shtm
4. http://www.javaeye.com/topic/206937
5. http://www.examda.com/ncre2/JAVA/bianc/20091022/085201677.html
6. http://www.javaeye.com/topic/416114
7. http://www.cnblogs.com/xiaoqin/archive/2007/08/06/844884.html
  对比
1. http://www.java2000.net/todoshow.jsp?id=111340
2. http://topic.csdn.net/u/20090108/15/234a458d-7944-4431-8b2e-d62b9e6f0915.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值