首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Design Pattern: Command 形式

2012-09-22 
Design Pattern: Command 模式Command定义n 将来自客户端的请求传入一个对象,无需了解这个请求激活的 动作

Design Pattern: Command 模式

Command定义
n 将来自客户端的请求传入一个对象,无需了解这个请求激活的 动作或有关接受这个请求的处理细节。

这是一种两台机器之间通讯联系性质的模式,类似传统过程语 言的 CallBack功能。

优点:
解耦了发送者和接受者之间联系。 发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。

不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.

将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.

显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,
从Command模式,我也发现设计模式一个"通病":好象喜欢将简单的问题复杂化, 喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性 可维护性 还有复用性.

如何使用?
具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:

典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象":

public interface Command {
  public abstract void execute ( );
}

如果您写过Java的Swing视窗程式,您可能使用过Command模式了,例如在您按下JMenuItem的“剪下”选项时,执行对JTextArea的选定文字之剪下动作,并将状态列设定为文件已修改状态。

在设计Swing时,设计人员是不可能知道使用Swing类别的人,在某个事件发生后所要执行的动作是什么的,他们采用了Command模式,以上面的需求作为例子,一个实作的片段可能像是这个样子:

menuCut.addActionListener(
??? new ActionListener() {
??????? public void actionPerformed(ActionEvent e) {
??????????? // textArea 是 JTextArea的一个实例
??????????? textArea.cut();??
???????? }
??? });

?
上面个这片段采用的是Java的匿名类别(Anonymous class),一个不具名的类别实作了ActionListener介面,它只有一个方法actionPerformed(),使用 addActionListener()为JMenuItem加入这个类别的实例,一但这个JMenuItem被按下,它就会调用 actionPerformed()方法,以执行您所定义的工作, UML 类别图如下所示:

使用Command模式,您可以根据实际的需求来调用执行的物件,至于执行的细节封装在事先定义好的方法(例如actionPerformed()方法,下面实际撰写个程式作为示范:
Invoker.java
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.java
public interface ICommand {    public void execute();} 

?

UpperCaseHello.java
public class UpperCaseHello implements ICommand {    private String name;        public UpperCaseHello(String name) {        this.name = name;        }        public void execute() {        System.out.println("HELLO, " + name.toUpperCase());    }} 

?

LowerCaseHello.java
public 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的请求:

Client.java
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中执行的物件了。

热点排行