Design Pattern: Command 模式
Command定义
n 将来自客户端的请求传入一个对象,无需了解这个请求激活的 动作或有关接受这个请求的处理细节。
这是一种两台机器之间通讯联系性质的模式,类似传统过程语 言的 CallBack功能。
优点:
解耦了发送者和接受者之间联系。 发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。
不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.
将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.
显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,
从Command模式,我也发现设计模式一个"通病":好象喜欢将简单的问题复杂化, 喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性 可维护性 还有复用性.
如何使用?
具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:
典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":
public interface Command {如果您写过Java的Swing视窗程式,您可能使用过Command模式了,例如在您按下JMenuItem的“剪下”选项时,执行对JTextArea的选定文字之剪下动作,并将状态列设定为文件已修改状态。
在设计Swing时,设计人员是不可能知道使用Swing类别的人,在某个事件发生后所要执行的动作是什么的,他们采用了Command模式,以上面的需求作为例子,一个实作的片段可能像是这个样子:
?
上面个这片段采用的是Java的匿名类别(Anonymous class),一个不具名的类别实作了ActionListener介面,它只有一个方法actionPerformed(),使用 addActionListener()为JMenuItem加入这个类别的实例,一但这个JMenuItem被按下,它就会调用 actionPerformed()方法,以执行您所定义的工作, UML 类别图如下所示:
import java.util.*;public class Invoker { private Map commands; public Invoker() { commands = new HashMap(); } public void addCommand(String commName, ICommand command) { commands.put(commName, command); } public void request(String commName) { ICommand command = (ICommand) commands.get(commName); command.execute(); }}
?
ICommand.javapublic interface ICommand { public void execute();}
?
UpperCaseHello.javapublic class UpperCaseHello implements ICommand { private String name; public UpperCaseHello(String name) { this.name = name; } public void execute() { System.out.println("HELLO, " + name.toUpperCase()); }}
?
LowerCaseHello.javapublic class LowerCaseHello implements ICommand { private String name; public LowerCaseHello(String name) { this.name = name; } public void execute() { System.out.println("hello, " + name.toLowerCase()); }}
Client模拟随机的请求,Invoker事先并不知道Client会发出什么需求,但它总是可以执行Client的请求:
public class Client { public static void main(String[] args) { Invoker invoker = new Invoker(); invoker.addCommand("JUSTIN", new UpperCaseHello("Justin")); invoker.addCommand("momor", new LowerCaseHello("momor")); // simulate random request String[] req = {"JUSTIN", "momor"}; while(true) { int i = (int) (Math.random() * 10) % 2; invoker.request(req[i]); } }}
?
像上面这种将请求封装起来的模式,就是Command模式,它将请求后的实作部份留待使用者实作,Command模式是个相当常见的模式,除了先前谈过的Swing视窗程式设计例子之外,现在许多Web MVC Framework 也都采用Command模式来设计Controller,在这样的例子中,Container就好比Command模式中Client的角色,而前端 Controller(例如JSP技术中通常会采用的Dispatcher Servlet)就相当于Invoker,而Action则相当于ICommand的角色,至于Receiver角色就是您封装在Action中执行的物件了。