java与模式_4.1命令模式(Java)

a019cc75d49d8325da025ac891014f85.png

面向对象中的使用关系,通常被称为Client/IServer(C/S) 结构。例如[1.3.1实验2:框架设计者]中SortTest→IntSort关系,SortTest作为Client依赖抽象的服务IntSort。Client类体中通常会见到这样的代码:

IServer s = (IServer) God.create("server");

s.foo();

然而,框架的设计者有时候发现,Client不知道应该依赖谁!Client不知道要依赖的类IServer,更不知道可以调用的方法foo()。例如要设计按钮/MyButton类,点击按钮后,用户可能想打开对话框、保存文件或关闭程序。此时不可能让MyButton依赖Java的终极类Object,那么MyButton依赖谁呢?

傻傻的幸福

MyButton(它是调用者/Invoker)傻乎乎地不知道消息接收者是谁,也不知道消息接收者有什么方法可以调用。于是,MyButton不管不顾地调用某个接口如Command的doSth()或exe()方法,通常,例程4-1中的两个类型属于框架/底层。由应用程序员设计的Command的子类型即子命令,指定MyButton需要依赖的某个服务类并调用服务类的某个方法。

例程 

而应用程序则设计了若干类型。如服务类可能是一个对话框MyDialog,或者Java的System类(将关闭程序)等等。而Command的子类型例如SaveCommand,将指定MyDialog完成某个任务。命令模式中的服务类在GoF中被称为接收者/Receiver。一般地,接收者可以是任意的类型。

例程 

很多人认为命令模式的优点,是完成消息发出者与执行者(Invoker/ Receiver)之间的解耦,并认为采用命令模式使得软件有更松散的耦合。从结果上看有一点道理,但事实上,使用命令模式不是希望Invoker和Receiver离婚,Invoker只有10岁,他完全就不知道它老婆将会是谁。耦都没有,咋解。

命令模式的意图,形象地说,是单身狗的幸福。通常而言,阅读应用了命令模式的源代码时,阅读者不会将Invoker与某个被隔离的Receiver联系起来。

命令模式与行为参数化

命令模式的核心,Command封装了一个命令/操作/行为/请求。但是例程4-1中的Command类层次的用法/意图,显然不同于行为参数化例子中的Condition、INext、IItem等类层次。

GoF对命令模式的定义是:『将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式的别名为动作(Action)模式或事务(Transaction)模式』。这是一种扩大命令模式的外延的做法,或者说夸大其词。因为行为参数化中Condition、INext、IItem等类层次都符合——甚至比例程4-1的Command类层次更加符合将“一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化”。

本节想强调的是,Receiver/接受者是命令模式中不可或缺的角色,接受者可以是任意类型,通常由子命令绑定消息接受者。如果仅仅将目光聚焦到Command类层次,命令模式就会与策略模式/行为参数化混淆。

例如,GoF在[5.2.9 一个命令对象应达到何种智能程度]中,提到:『命令对象的能力可大可小。一个极端是它仅确定一个接收者和执行该请求的动作。另一极端是它自己实现所有功能,根本不需要额外的接收者对象。当需要定义与已有的类无关的命令,当没有合适的接收者,或当一个命令隐式地知道它的接收者时,可以使用后一极端方式。』

例程4-2反映的是前一个极端。SaveCommand确定了一个接收者MyDialog,在完成exe()时转身要求接收者执行foo()。而在行为参数化的例子中,显然Condition的子类自己实现所有功能,根本不需要额外的接收者对象。面对Condition这种的应用方式,是否按照GoF的说法,错误地将它归结于命令模式?

正因为GoF扩大了命令模式的外延,他们在[5.2.4适用性]中写到:『你可用过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。 Command模式是回调机制的一个面向对象的替代品』。注意:GoF中使用的术语回调机制,按照本书的术语,是指行为参数化。

本书明显不希望将命令模式与策略模式混为一谈。把命令模式放在本章,就是要强调两者的差异:命令模式中,Invoker不知道Receiver的任何信息;策略模式中,Client知道并依赖IServer。这是一个前提。在此基础上,既然Invoker不知道Receiver的任何信息,那么有没有Receiver,对于Invoker来说也就无关紧要了。策略模式中,抽象方法的名字是与应用环境相关的;而命令模式中,Command封装到抽象方法的名字是普适的。

基于Receiver/接受者在命令模式中的重要性角色。下面讨论一下Receiver。Receiver可以是任意类型,包括:

①固定的具体类。如SaveCommand指定的new MyDialog()。

②抽象类型。如果SaveCommand中希望依赖一个抽象类型如MyDialog2,可以使用God等工具类创建MyDialog2。

③临时的接受者。由于框架中Invoker仅仅依赖Command,程序员可以在Client中创建新的Command子类型。MyButton不知道消息接受者的任何信息,所以Client中是否设计一个消息接受者如内部类Chowhound,对于MyButton的设计者无关紧要。框架设计者只知道我的exe()有Receiver,至于是什么类无所谓——也不反对应用程序员把子命令作为Receiver。

例程 

命令模式的经典例子,如在Java线程框架中,(作为Invoker//调用者的)Thread不知道最终调用谁,于是Thread把要执行的全部代码,放入一个特殊的run()方法中,该方法由接口java.lang.Runnable定义。

public 

万能的适配目标?

★Command模式:Invoker不知道应该依赖谁,因此将普适方法exe ()作为适配目标。Command模式是适配器模式的特例。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值