Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1964881
  • 博文数量: 606
  • 博客积分: 9991
  • 博客等级: 中将
  • 技术积分: 5725
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-17 19:07
文章分类

全部博文(606)

文章存档

2011年(10)

2010年(67)

2009年(155)

2008年(386)

分类:

2008-11-01 02:08:03

一、 Command模式定义:
    Command(CoR)模式也叫命令模式,是由GoF提出的23种软件设计模式的一种。
    Command模式是行为模式之一,Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。
    将一个请求封装为一个对象,从而使你不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
二、 模式解说
    Commad模式是一种对象行为模式,它可以对发送者(sender)和接收者(receiver)完全解耦(decoupling)。("发送者" 是请求操作的对象,"接收者" 是接收请求并执行某操作的对象。有了 "解耦",发送者对接收者的接口一无所知。)这里,"请求"(request)这个术语指的是要被执行的命令。Command模式还让我们可以对 "何时" 以及 "如何" 完成请求进行改变。因此,Command模式为我们提供了灵活性和可扩展性。
 
三、Command模式参与者描述
 
 
1、客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者。
2、命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色。
3、具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现Execute()方法,负责调用接收考的相应操作。Execute()方法通常叫做执方法。
4、请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
5、接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

 四、怎么使用?
    1) 定义一个Command接口,接口中有一个统一的方法,这就是将请求/命令封装为对象。
    2) 定义具体不同命令类ConcreteCommand实现Command接口。
    3) 定义一个命令的调用角色Invoker。
    4) 定义一个命令执行状态的接收者Receiver(非必须)。
 
五、Command模式的应用场景
 
在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。
但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。
- 整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。
- 调用前后需要对调用参数进行某些处理。
- 调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。


一般来说,Command模式通常可应用到以下场景:

Multi-level undo(多级undo操作)
如果系统需要实现多级回退操作,这时如果所有用户的操作都以command对象的形式实现,系统可以简单地用stack来保存最近执行的命令,如果用户需要执行undo操作,系统只需简单地popup一个最近的command对象然后执行它的undo()方法既可。

Transactional behavior(原子事务行为)
借助command模式,可以简单地实现一个具有原子事务的行为。当一个事务失败时,往往需要回退到执行前的状态,可以借助command对象保存这种状态,简单地处理回退操作。

Progress bars(状态条)
假如系统需要按顺序执行一系列的命令操作,如果每个command对象都提供一个getEstimatedDuration()方法,那么系统可以简单地评估执行状态并显示出合适的状态条。

Wizards(导航)
通常一个使用多个wizard页面来共同完成一个简单动作。一个自然的方法是使用一个command对象来封装wizard过程,该command对象在第一个wizard页面显示时被创建,每个wizard页面接收用户输入并设置到该command对象中,当最后一个wizard页面用户按下“Finish”按钮时,可以简单地触发一个事件调用execute()方法执行整个动作。通过这种方法,command类不包含任何跟用户界面有关的代码,可以分离用户界面与具体的处理逻辑。

GUI buttons and menu items(GUI按钮与菜单条等等)
Swing系统里,用户可以通过工具条按钮,菜单按钮执行命令,可以用command对象来封装命令的执行。

Thread pools(线程池)
通常一个典型的线程池实现类可能有一个名为addTask()的public方法,用来添加一项工作任务到任务队列中。该任务队列中的所有任务可以用command对象来封装,通常这些command对象会实现一个通用的接口比如java.lang.Runnable。

Macro recording(宏纪录)
可以用command对象来封装用户的一个操作,这样系统可以简单地通过队列保存一系列的command对象的状态就可以记录用户的连续操作。这样通过执行队列中的command对象,就可以完成“Play back”操作了。

Networking
通过网络发送command命令到其他机器上运行。

Parallel Processing(并发处理)
当一个调用共享某个资源并被多个线程并发处理时。

等等

六:Java实例1

Client         :开关灯的人
Invoker      :遥控器
Command  :电信号
具体命令 :遥控器上的按键对应的不同的电信号(ConcreteCommand
Receiver    :电灯

a. receiver包----接收者包,在这里,light就是接收者,由它执行最后的动作。

Java代码 复制代码

  1. package reciever;   
  2.   
  3. public class Light {   
  4.     //private static final int open = 0 ;   
  5.     //private static final int close = 1 ;   
  6.        
  7.     public Light(){}    
  8.        
  9.     public void onlight(){   
  10.         System.out.println("The light is open!") ;   
  11.     }   
  12.     public void offlight(){   
  13.         System.out.println("The light is close!") ;   
  14.     }   
  15. }  


b. command包----电信号

命令对象的execute方法执行了接受者(Light)的一个或者几个action,在由调用者control确定什么时候执行execute。 

Java代码 复制代码

  1.   package command;   
  2.   
  3. /*  
  4.  * Interface  
  5.  */  
  6. public interface ICommand {   
  7.     void execute() ;   
  8.     void undo() ;   
  9. }

Java代码 复制代码

  1. package command;   
  2.   
  3. import reciever.Light;   
  4.   
  5. /*  
  6.  * 灯关的命令对象  
  7.  */  
  8. public class LightOffCommand implements ICommand {   
  9.     private Light light ;   
  10.        
  11.     public LightOffCommand(Light light){   
  12.         this.light = light ;   
  13.     }   
  14.        
  15.     public void execute() {   
  16.         this.light.offlight() ;   
  17.     }   
  18.   
  19.     public void undo() {   
  20.         this.light.onlight() ;   
  21.     }   
  22.   

Java代码 复制代码

  1. package command;   
  2.   
  3. import reciever.Light;   
  4.   
  5. /*  
  6.  * 灯开的命令对象  
  7.  */  
  8. public class LightOnCommand implements ICommand {   
  9.   
  10.     private Light light ;   
  11.        
  12.     public LightOnCommand(Light light){   
  13.         this.light = light ;   
  14.     }   
  15.        
  16.     public void execute() {   
  17.         this.light.onlight() ;   
  18.     }   
  19.   
  20.     public void undo() {   
  21.         this.light.offlight() ;   
  22.     }   
  23.   
  24. }

Java代码 复制代码

  1. package command;   
  2. /*  
  3.  * 空对象。null object  
  4.  */  
  5. public class NoCommand implements ICommand {   
  6.     //do nothing   
  7.     public void execute() {   
  8.     }   
  9.   
  10.     public void undo() {   
  11.     }   
  12.   
  13. }  

c. requester---指遥控器。

通过setCommand()方法来获取ICommand,再由子类“决定”需要的哪个Command。通过几个Button方法,来决定什么时候执行相应的事件。接受者作出相应地反映(灯亮,灯灭)。这里即可以体会到,遥控器和灯是解耦的。

 
Java代码 复制代码
  1. package requester;   
  2.   
  3. import command.ICommand;   
  4. import command.NoCommand;   
  5.   
  6. /*  
  7.  * 调用者  
  8.  */  
  9. public class RemoteControl {   
  10.     //undocommand变量用来村粗light的最后一次的状态   
  11.     private ICommand oncommand ,offcommand ,undocommand ;   
  12.     public RemoteControl(){   
  13.         this.oncommand = new NoCommand() ;   
  14.         this.offcommand = new NoCommand() ;   
  15.         this.undocommand = new NoCommand() ;   
  16.     }   
  17.        
  18.     public void setCommand(ICommand oncommand,ICommand offcommand){   
  19.         this.oncommand = oncommand ;   
  20.         this.offcommand = offcommand ;   
  21.     }   
  22.        
  23.     public void onButtonWasPressed(){   
  24.         oncommand.execute() ;   
  25.         this.undocommand = this.oncommand ;   
  26.     }   
  27.        
  28.     public void offButtonWasPressed(){   
  29.         offcommand.execute() ;   
  30.         this.undocommand = this.offcommand ;   
  31.     }   
  32.        
  33.     public void undo(){   
  34.         this.undocommand.undo() ;//取消操作   
  35.     }   
  36. }  


d. 测试类--人

Java代码 复制代码

  1. package test;   
  2.   
  3. import command.ICommand;   
  4. import command.LightOffCommand;   
  5. import command.LightOnCommand;   
  6.   
  7. import reciever.Light;   
  8. import requester.RemoteControl;   
  9.   
  10. /*  
  11.  * Test Class  
  12.  */  
  13. public class ControlTest {   
  14.     public static void main(String[] args){   
  15.     //receiver   
  16.     Light light = new Light() ;   
  17.        
  18.     //command   
  19.     ICommand lightOff = new LightOffCommand(light) ;   
  20.     ICommand lightOn = new LightOnCommand(light) ;   
  21.        
  22.     //control   
  23.     RemoteControl control = new RemoteControl() ;   
  24.     control.setCommand(lightOn , lightOff) ;   
  25.     control.onButtonWasPressed() ;   
  26.     control.offButtonWasPressed() ;   
  27.        
  28.     control.undo() ;   
  29.     //control.setCommand(lightOff) ;   
  30.     //control.offButtonWasPressed() ;   
  31.     }   
  32. }  
  33.  e.控制台输出:
    The light is open!
    The light is close!
    The light is open!
Java实例2
 
使用Command模式实现一个简单的execute/undo操作。
在该范例中,我们有一个简单的操作:对字符串做append操作,这个操作由Receiver类实现;另外,我们需要记录操作历史,并能简单加以回退(undo),所以我们采用Command模式实现。
文件一览:
Client  : 测试类
Command : Command抽象类
UndoableCommand: 支持undo操作的Command抽象类,该类是Command类的子类
ConcreteCommand: 具体的UndoableCommand实现类,该类继承UndoableCommand类,所以支持undo操作
CommandManager: Command管理类。该类使用Stack来管理执行过的Command对象,并提供executeCommand()与undoCommand()方法
Receiver: 执行任务的目标类
Invoker: 这个类在我们的例里没有被用到,但我们仍给出了它的一个参考实现,不过用注释表示它不可用

代码:
import java.util.Stack;

public class Client {

    /**
     * Test Command Pattern
     *
     */

    public static void main(String[] args) {
        CommandManager commandMgr = new CommandManager();
        
        Receiver receiver = new Receiver();

        System.out.println("--- execute command ---");
        Command commandAaa = new ConcreteCommand(receiver, "aaa");
        commandMgr.executeCommand(commandAaa);
        
        Command commandBbb = new ConcreteCommand(receiver, "bbb");
        commandMgr.executeCommand(commandBbb);
        
        Command commandCcc = new ConcreteCommand(receiver, "ccc");
        commandMgr.executeCommand(commandCcc);
        
        Command commandDdd = new ConcreteCommand(receiver, "ddd");
        commandMgr.executeCommand(commandDdd);
        
        System.out.println(receiver.getData());
        
        System.out.println("-- undo ---");
        commandMgr.undoCommand();
        commandMgr.undoCommand();
        System.out.println(receiver.getData());
    }
}


/**
* Command
* abstract command class
*
*/

abstract class Command {
    protected Receiver receiver;

    protected String param;

    public Command(Receiver receiver, String expr) {
        this.receiver = receiver;
        this.param = expr;
    }

    abstract public void execute();
}

/**
* UndoableCommand
* abstract undo supportable command class which extends from Command class
*
*/

abstract class UndoableCommand extends Command {

    public UndoableCommand(Receiver receiver, String expr) {
        super(receiver, expr);
    }

    abstract public void undo();
}


/**
* ConcreteCommand
* concrete command class which extends from UndoableCommand
*
*/

class ConcreteCommand extends UndoableCommand {
    private String previousData = null;

    public ConcreteCommand(Receiver receiver, String expr) {
        super(receiver, expr);
    }

    @Override
    public void execute() {
        previousData = receiver.getData();
        receiver.append(this.param);
    }

    @Override
    public void undo() {
        receiver.setData(previousData);
    }
}


/**
* CommandManager
* Command Manager class which stack the exe
*
*/

class CommandManager {
    private Stack commandStack = new Stack();

    public void executeCommand(Command cmd) {
        cmd.execute();
        if (cmd instanceof UndoableCommand) {
            commandStack.push(cmd);
        }
    }

    public void undoCommand() {
        if (commandStack.size() > 0) {
            UndoableCommand cmd = (UndoableCommand) commandStack.pop();
            cmd.undo();
        } else {
            throw new UnsupportedOperationException("");
        }
    }
}


/**
* Receiver
* target object
*
*/

class Receiver {
    private String data = "";

    public void append(String expr) {
        data += expr;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

/**

class Invoker {
    private Command command;

    public void setCommand(Command command) {
        this.command = command;
    }

    public void executeCommand() {
        command.execute();
    }
}

*/




执行Client,输出结果:
C:\Command>javac *.java
C:\Command>java Client
--- execute command ---
aaabbbcccddd
-- undo ---
aaabbb
C:\Command>
我们可以看到,使用Command模式非常简单地就实现了undo操作。
 
七: 总结

命令模式的优点:降低Client和命令接受者的耦合,使命令请求和命令执行的对象分割,便于修改和扩张,便于聚合多个命令。

命令模式的缺点:造成出现过多的具体命令类,太多文件。
 
 
参考资料:
                    http://lchlrb.javaeye.com/blog/237210
                   
阅读(923) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~