Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3334437
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类: Java

2009-12-01 10:29:24

1.什么时候使用command模式
   1、使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
   2、需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
   3、系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
   4、如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
   5、一个系统需要支持交易(Transaction)。一个交易结构封装了一组数据更新命令。使用命令模式来实现交易结构可以使系统增加新的交易类型。

2.command模式的几个要素
   命令模式又称为行动(Action)模式或交易(Transaction)模式。命令模式把一个请求或者操作封装到一个对象中。命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
   命令模式涉及到五个角色,它们分别是:
      客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者。
      命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色。
      具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现Execute()方法,负责调用接收考的相应操作。Execute()方法通常叫做执方法。
      请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
      接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

     协作说明:
     客户端创建了一个具体的Command对象并指定了其接收者。调用者对象存储了此具体的Command对象。调用者对象通过执行Command对象的 Execute方法来实现当前请求。如果命令是可以撤销时,具体对象在调用执行方法前将存储相应的状态以用来命令此请求。具体的Command对象调用其接收者的方法从而来实现相应请求。

3.代码举例
public class CommandDemo {
    public static void main(String[] args) {
        Reciver reciver = new Reciver();//有个人能执行某个命令,各尽其能
        Command command = new ConcreteCommand(receiver);//领导将这个人与这个命令绑定,一但要执行这个命令,就找他,人尽其用
        Invoker invoker= new Invoker();//负责什么时候传达命令,而且还不用管是什么命令,相当于管家,只管传令
        Invoker.add(command);//领导让他传达一个命令
        Invoker.action();//他就开始传达
    }
}

public interface Command {
    public void execute();
}

public class ConcreteCommand implements Command {
    Reciver reciver;

    /**
     * 需命令的真正实现者即reciever
     *
     * @param programer
     */
    public ConcreteCommand(Reciver reciver) {
        this.reciver= reciver;
    }

    public void execute() {
        reciver.doing();
    }
}

public class Reciver {
    /**
     * reciver是具体需求的真正执行者
     */
    public void doing() {
        System.out.println("design...coding...test...shell out");
    }
}

public class Invoker{
    private Command cmd;

    public void add(Command cmd) {
        this.cmd = cmd;
    }
    /**
     * 对于命令模式而言,它只需知道启动命令的执行既可,与真正的实现者解耦 且此处使用的是Command接口
     */
    public void action() {
        cmd.execute();
    }
}

   命令模式有如下的一些好处:
        1:命令模式将调用者对象与接收对象解耦(调用与实现解耦)。调用者实现功能时只需调用Command接口的Execute方法。
        2:具体的Commands对象是第一层对象,它们可以像其他对象一样被扩展或继承。
        3:你可以将多个Commands对象聚合成一个组合命令。组合命令也是组合对象模式的一个实例,将命令排队也是其的一种特殊情况。
        4:你可以很容易的添加新的命令,因为你并不需要修改现有的代码。这也符合了开闭原则,对修改关闭,对扩展开放。

4.命令模式应用举例
4.1玉帝传美猴王上天
    命令模式不是新的发明,在美猴王大闹天宫之前就有了。那时玉帝命令太白金星召美猴王上天:"金星径入(水帘洞)当中,面南立定道:'我是西方太白金星,奉玉帝招安圣旨,下界请你上大,拜受仙录。'"玉帝是系统的客户端,太白金星是命令的发出者,猴王是命令的接收者,圣旨就是命令。玉帝的这一道命令就是要求猴王到上界报到。玉帝只管发出命令,而不管命令是怎样传达到美猴王的。太白金星负责将圣旨传到,可是美猴王怎么执行圣旨、何时执行圣旨是美猴王自己的事。果不然,个久美猴王就大闹了天宫。

这个模拟系统的设计如下:


4.2使用命令模式演示了一个简单的计算器,并允许执行undo与redo

   注意:"operator"在C#中是关键词,所以在前面添加一个"@"将其变为标识符。

   1. // Command pattern -- Real World example 
   2. using System;
   3. using System.Collections;
   4. // "Command"
   5. abstract class Command
   6. {
   7.  // Methods
   8.  abstract public void Execute();
   9.  abstract public void UnExecute();
  10. }
  11. // "ConcreteCommand"
  12. class CalculatorCommand : Command
  13. {
  14.  // Fields
  15.  char @operator;
  16.  int operand;
  17.  Calculator calculator;
  18.  // Constructor
  19.  public CalculatorCommand( Calculator calculator,
  20.   char @operator, int operand )
  21.  {
  22.   this.calculator = calculator;
  23.   this.@operator = @operator;
  24.   this.operand = operand;
  25.  }
  26.  // Properties
  27.  public char Operator
  28.  {
  29.   set{ @operator = value; }
  30.  }
  31.  public int Operand
  32.  {
  33.   set{ operand = value; }
  34.  }
  35.  // Methods
  36.  override public void Execute()
  37.  {
  38.   calculator.Operation( @operator, operand );
  39.  }
  40.  
  41.  override public void UnExecute()
  42.  {
  43.   calculator.Operation( Undo( @operator ), operand );
  44.  }
  45.  // Private helper function
  46.  private char Undo( char @operator )
  47.  {
  48.   char undo = ' ';
  49.   switch( @operator )
  50.   {
  51.    case '+': undo = '-'; break;
  52.    case '-': undo = '+'; break;
  53.    case '*': undo = '/'; break;
  54.    case '/': undo = '*'; break;
  55.   }
  56.   return undo;
  57.  }
  58. }
  59. // "Receiver"
  60. class Calculator
  61. {
  62.  // Fields
  63.  private int total = 0;
  64.  // Methods
  65.  public void Operation( char @operator, int operand )
  66.  {
  67.   switch( @operator )
  68.   {
  69.    case '+': total += operand; break;
  70.    case '-': total -= operand; break;
  71.    case '*': total *= operand; break;
  72.    case '/': total /= operand; break;
  73.   }
  74.   Console.WriteLine( "Total = {0} (following {1} {2})",
  75.    total, @operator, operand );
  76.  }
  77. }
  78. // "Invoker"
  79. class User
  80. {
  81.  // Fields
  82.  private Calculator calculator = new Calculator();
  83.  private ArrayList commands = new ArrayList();
  84.  private int current = 0;
  85.  // Methods
  86.  public void Redo( int levels )
  87.  {
  88.   Console.WriteLine( "---- Redo {0} levels ", levels );
  89.   // Perform redo operations
  90.   for( int i = 0; i < levels; i++ )
  91.    if( current < commands.Count - 1 )
  92.     ((Command)commands[ current++ ]).Execute();
  93.  }
  94.  public void Undo( int levels )
  95.  {
  96.   Console.WriteLine( "---- Undo {0} levels ", levels );
  97.   // Perform undo operations
  98.   for( int i = 0; i < levels; i++ )
  99.    if( current > 0 )
 100.     ((Command)commands[ --current ]).UnExecute();
 101.  }
 102.  public void Compute( char @operator, int operand )
 103.  {
 104.   // Create command operation and execute it
 105.   Command command = new CalculatorCommand(
 106.    calculator, @operator, operand );
 107.   command.Execute();
 108.   // Add command to undo list
 109.   commands.Add( command );
 110.   current++;
 111.  }
 112. }
 113. /**////
 114. /// CommandApp test
 115. ///

 116. public class Client
 117. {
 118.  public static void Main( string[] args )
 119.  {
 120.   // Create user and let her compute
 121.   User user = new User();
 122.   user.Compute( '+', 100 );
 123.   user.Compute( '-', 50 );
 124.   user.Compute( '*', 10 );
 125.   user.Compute( '/', 2 );
 126.   // Undo and then redo some commands
 127.   user.Undo( 4 );
 128.   user.Redo( 3 );
 129.  }
 130. }

5.命令模式的优点和缺点

优点:
   命令模式使新的命令很容易地被加入到系统里。
   允许接收请求的一方决定是否要否决(Veto)请求。
   能较容易地设计一个命令队列。
   可以容易地实现对请求的Undo和Redo。
   在需要的情况下,可以较容易地将命令记入日志。
   命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。
   命令类与其他任何别的类一样,可以修改和推广。
   你可以把命令对象聚合在一起,合成为合成命令。比如宏命令便是合成命令的例子。合成命令是合成模式的应用。
   由于加进新的具体命令类不影响类,因此增加新的具体命令类很容易。

缺点:
   使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

参考文献:
1.设计模式之Command设计模式.
2.设计模式之命令模式-Command pattern.
阅读(1258) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~