Command模式

来源:互联网 发布:韩国网络作家 编辑:程序博客网 时间:2024/06/06 18:00

Command模式是指用一个对象来表示和封装要调用的方法的所有信息,这个方法会在之后的某个时间执行。这些信息包括方法名字, 那个对象拥有这个方法,方法的参数。这种模式中有三种角色,client, invoker 和 receiver。 client用来实例化command对象,并提供被调用方法的所有信息。invoker会决定什么时候执行command对象对应的实际方法。receiver是指拥有该实际方法的实例,主要的逻辑处理也将在receiver拥有的这个方法里面执行。


使用command模式可以很容易构造通用的组件,这些组件可用于实现方法的代理,方法序列,或需要在将来的某个特定时间执行的方法。调用者(invoker)不需要知道实际被调用方法的所有者是谁,需要哪些参数等。


用途:

1. 复杂的undo(Multi-level undo)

如果所有用户的操作都以一个command对象来实现,程序中可以保留一个存放command对象的堆栈。如果用户需要取消(undo)一个操作,程序可以堆栈中取出最近的command,运行command对象中对应的undo方法。


2. 事务处理(Transactional behavior)

和undo的实现类似,程序中可以保存一个事务内相关操作的列表,如果其中一个操作失败,那么则回滚列表内其他已经执行了的操作。


3. 进度条的实现(Progress bars)

假设一个程序中要顺序执行一系列操作,如果每一个操作(具体的command对象)都实现了getEstimatedDuration()方法,那么在此程序中就很容易计算出这一系列操作完成需要的总时间。进而进度条就可以反映出这一系列操作的完成情况。


4. Wizards

wizards通常表示一个配置需要分几个页面(步骤)来完成,比如填写简历,第一页填写基本信息,下一页是工作经历,等等。利用command模式的话,就可以把每一步用一个command对象来实现,当用户最后完成所有步骤并确认时,这时才依次调用command的execute方法。


5. 按钮和菜单项(GUI buttons and menu items)

在Swing中,一个Action就是一个command对象。一个Action可能需要包含一个icon,快捷键,tooltip等信息。一个工具栏或菜单的实现就需要使用Action对象。


6. 线程池(Thread pools)

通常一个线程池类会有一个addTask()方法,用来添加一个任务到一个内部队列中,在队列中task就实现为一个command对象,通常这个时候,command类会实现java.lang.Runnable接口,可以使线程池执行对应的command,线程池不需要知道所执行的任务(即command)的具体信息。


7. 网络应用(networking)

command对象可以通过网络传送到其他机器,比如在一些网络游戏中的应用。


8. 并行处理(Parallel Processing)

任务可以被实现为一个command对象,并作为一个共享资源,被多个线程同时调用,例如在Master/worker模式下,还可以被远程调用。


command模式的结构:

图解:

Command为接口,ConcreteCommand实现Command接口,并实现execute方法.

在ConcreteCommand.execute()方法会调用Receiver中的Action()方法,真正的逻辑实现是发生在Receiver.Action()方法中,所以ConcreteCommand类是依赖与Receiver的。

在Invoker中会保存一系列需要执行的Command对象,所以Invoker和Command是组合的关系。

在Client中会初始化Receiver和Command对象,所以client和Receiver和ConcreteCommand都是依赖的关系。

(注:可以是由client来把command对象添加到invoker的command集合中,或是有invoker主动监听的方式,或是别的方式,这里视情况而定)。


实例:

电源开关的例子:

/*the Command interface*/public interface Command {   void execute();}  /*the Invoker class*/import java.util.List;import java.util.ArrayList; public class Switch {    private List<Command> history = new ArrayList<Command>();    public Switch() {   }    public void storeAndExecute(Command cmd) {      this.history.add(cmd); // optional       cmd.execute();           } }  /*the Receiver class*/public class Light {    public Light() {   }    public void turnOn() {      System.out.println("The light is on");   }    public void turnOff() {      System.out.println("The light is off");   } }  /*the Command for turning on the light - ConcreteCommand #1*/public class FlipUpCommand implements Command {    private Light theLight;    public FlipUpCommand(Light light) {      this.theLight = light;   }    public void execute(){      theLight.turnOn();   } }  /*the Command for turning off the light - ConcreteCommand #2*/public class FlipDownCommand implements Command {    private Light theLight;    public FlipDownCommand(Light light) {      this.theLight = light;   }    public void execute() {      theLight.turnOff();   } }  /*The test class or client*/public class PressSwitch {    public static void main(String[] args){      Light lamp = new Light();      Command switchUp = new FlipUpCommand(lamp);      Command switchDown = new FlipDownCommand(lamp);       Switch s = new Switch();       try {         if (args[0].equalsIgnoreCase("ON")) {            s.storeAndExecute(switchUp);            System.exit(0);         }         if (args[0].equalsIgnoreCase("OFF")) {            s.storeAndExecute(switchDown);            System.exit(0);         }         System.out.println("Argument \"ON\" or \"OFF\" is required.");      } catch (Exception e) {         System.out.println("Argument's required.");      }   } }



原创粉丝点击