程序员人生 网站导航

3.4 命令模式(5.2)

栏目:互联网时间:2014-09-07 07:24:14

1. 引子

假定电视机/TV有方法open()、close()和changeChannel()用于打开、关闭和切换电视频道,而遥控器/Controller对TV的操作,通常使用消息传递/方法调用表达式。

package method.command; /** * @author yqj2065 * @version 0.1 */ public class Controller1{ public static void foo(){ TV tv = new TV(); tv.open(); tv.changeChannel(); tv.close(); } }
面向对象中的服务请求,或者说消息传递表达式如 
tv.open();
与命令式语言如C的函数调用的最大不同,是消息传递表达式包含消息接收者tv,另外再加上open()。

图3-3 消息传递、C/S结构

命令模式则是追求一种千秋万代一统江湖的服务请求方式。

  •  Controller不愿意记住方法名。open()、close()、changeChannel()这些东西?以后有没有更多的方法可以调用,或者说更多的服务可以请求呢?所以,我不愿意被方法名限制,我以exe ()作为普适的方法名,exe()被“方法对象化”为Command――使用Command封装exe ()。
  • Controller不愿意知道消息接收者是谁。司令员下命令打下这个山头,他会不会考虑要那个连长去带人攻打呢。管你谁谁,打下这个山头才是司令员关心的。

 学习命令模式稍有难度,毕竟一统江湖的事情,总得有点难度。

package method.command; public interface Command{ public void exe(); }

下面,是直接给出命令模式,让大家死记硬背地理解呢?还是从0开始,研究一下Controller如何才能够忘记/无视被调的方法名

2. 命令与执行

先直接给出命令模式的例子吧。

既然有了Command,按照多态也好,难度系数为0的策略模式也罢,tv的open()演变成Command的子类OpenCommand

OpenCommand有私有成员TV tv,而OpenCommand的exe()干什么?显然只需要一条语句tv.open()。代码自己随手写吧。

因为我们拥有依赖注入工具tool.God,(注意:在我的博客的很多的文章中,都使用了该工具,但是类名用过FromPropertyFile、IoC、God,所在包也有所变化,懒得逐一修改相关博文了。代码的意思很清楚,读者自己对应修改一下),因而代码

package method.command; import tool.God; public class Controller{ public static void test() { Command c1 = (Command)God.create("open"); c1.exe(); } }
Controller仅仅知道Command对象,Controller下的命令为字符串"open",God根据字符串"open"创建method.command.OpenCommand对象。

忽略一切细节,Controller仅依赖Command,对照的,Controller1依赖TV,和TV的现有操作/方法名


命令模式的基本结构

①命令模式的核心,是封装普适方法exe ()的Command。通过它及其子类,将如图3-3所示的通常的服务请求中的请求发送者和接收者完全解耦,或者说将通常的C/S结构的C与S解耦。

C仅仅依赖于Command。而OpenCommand依赖于Command和S。

所以,我们常常说Command采用了命令模式。或许应该说 以Command同志为核心的命令模式?

②依赖于Command的各种类(不包括其子类),在《设计模式》中称为调用者(Invoker),它们是命令的发出者。借助反射机制或依赖注入模式或依赖注入工具类tool.God,调用者可以发出Command的各种子类封装的命令,而且不需要知道最终调用的是什么方法名、不需要知道最终谁执行。
如果调用者突发奇想地发出(需要)新的命令,可以编写Command的新子类以及执行者。

package method.command; public class EatCommand implements Command{ @Override public void exe() { new Chowhound().eat(); } private class Chowhound{//吃货 public void eat(){System.out.println("好吃");} } }

在配置文件中添加eat =method.command.EatCommand

则修改Controller的"open",即c1 =(Command)God.create("eat");就ok。

③具体命令类是封装的命令的Command的各种子类,如OpenCommand。在override/改写exe ()时,将命令的执行者与某一操作绑定如tv.open()。虽然简单起见,OpenCommand中通过成员变量如电视/TV设定了执行者,事实上,可以通过依赖注入模式,按照配置文件方便地指定消息接收者的类型例如OpenHandler。

3.吐槽 《设计模式・5.2》

《设计模式》中,给命令模式(Command Pattern)的定义/意图比较繁琐。正如刀可以砍人,你把刀玩出花样来――来个回马刀都可以,刀的基本作用还是砍人。

我的定义是:以封装普适方法的命令类层次为桥梁,将通常C/S结构的C与S解耦

既然命令模式使得C仅仅依赖于Command,它不知道S为何物,也不知道S的接口所以

C下达的一系列命令,你可以组合成一个队列、可以组合成一个批命令;也可以反之,将C下达的一个命令分解成若干具体的命令;

对于命令执行前后的变化加以监控,你可以实现undo或redo;如果命令只是改变一个页面的颜色,你很容易undo/取消操作;如果命令导致手榴弹炸了一个房屋,omg,你undo就很麻烦。

你可以玩出其他花样。比如C下达的一个命令open,对于接收者为TV,就打开电视;如果配置的接收者为一个连长,他就打开/攻占一座城门。

你可以玩出更多的花样……


续  返回目录

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐