每周学点设计模式(3)——命令模式

概述

在面向对象编程中,命令模式属于行为型(behavior)设计模式,用于“行为发送者”与“行为接收者”之间的解耦。(行为发送者为发起某个操作的对象,接收者为执行该操作请求的对象)。命令模式的结构如下图所示:

命令模式结构图

命令模式的常见使用场景有:

  • Java 菜单项和按钮的动作处理
  • 为宏(复合命令)提供支持(记录和回退的宏操作)
  • “undo”操作
  • Java进度条
  • 日志、请求队列等

使用命令模式实现事务

假定需要TCP端口连接的用于接受和处理用户请求事务的服务器,这些事务包含很多命令。如果使用switch语句在case中调用每个命令,会导致程序耦合度增加,不符合面向对象设计思想。将这些命令封装为对象将请求者和接受者解耦,这样的设计更加合适。

import java.util.*;
final class CommandReceiver {
  private int[] c;
  private CommandArgument a;
     private CommandReceiver(){
       c = new int[2];
     }
     private static CommandReceiver cr = new CommandReceiver();
     public static CommandReceiver getHandle() {
     return cr;
     }
     public void setCommandArgument(CommandArgument a) {
     this.a = a;
     }
     public void methAdd() {
     c = a.getArguments();
         System.out.println("The result is " + (c[0]+c[1]));
     }
     public void methSubtract() {
     c = a.getArguments();
         System.out.println("The result is " + (c[0]-c[1]));
     }
}

命令模式代码类图

CommandReceiver是命令接收者角色,实现了所有的命令处理方法,同时它作为单例实现。

 class CommandManager {
   private Command myCommand;
   public CommandManager(Command  myCommand) {
     this.myCommand  =  myCommand ;    
   }
   public void runCommands( ) {
            myCommand.execute();     
   }
 }

CommandManager是调用者角色,它的私有变量myCommand传入TransactionCommand对象。当runCommands()被调用时,选择合适的TransactionCommand对象的方法。

class TransactionCommand implements Command {
  private CommandReceiver commandreceiver;
  private Vector commandnamelist,commandargumentlist; 
  private String commandname;
  private CommandArgument commandargument;
  private Command command;
  public TransactionCommand () {
    this(null,null);
  }
  public TransactionCommand ( Vector  commandnamelist, Vector
commandargumentlist){
    this.commandnamelist = commandnamelist;
    this.commandargumentlist = commandargumentlist;
    commandreceiver =  CommandReceiver.getHandle();  
  }
  public void execute( ) {
    for (int i = 0; i < commandnamelist.size(); i++) {
      commandname = (String)(commandnamelist.get(i));
      commandargument = (CommandArgument)((commandargumentlist.get(i)));
      commandreceiver.setCommandArgument(commandargument);
      String classname = commandname + "Command";
         try {
           Class cls = Class.forName(classname);
           command = (Command) cls.newInstance();
         }
         catch (Throwable e) {   
                  System.err.println(e);
         }
      command.execute();
    } 
  }
}
 class AddCommand extends TransactionCommand {
   private CommandReceiver cr;
   public AddCommand () {
      cr = CommandReceiver.getHandle();  
   }  
   public void execute( ) {  
     cr.methAdd();  
   }   
 }
 class SubtractCommand extends TransactionCommand {
   private CommandReceiver cr;
   public SubtractCommand () {
      cr = CommandReceiver.getHandle();  
   }
   public void execute( ) {
     cr.methSubtract();
   }   
 }

TransactionCommand对象的execute()操作根据传递的命令名称列表的内容动态加载需要的命令子类(子类命名时遵守操作名 + “Command”的命名习惯)。

命令和参数存储在list中,封装在TransactionCommand对象中。

 class CommandArgument {
   private int[] args;
   CommandArgument() {
     args = new int[2];
   }
   public int[] getArguments() {
    return args;
   }
   public void setArgument(int i1, int i2) {
         args[0] = i1; args[1] = i2;
   }
 }

CommandArgument是辅助类,用来保存命令参数。

 public class TestTransactionCommand {
   private  Vector clist,alist; 
   public TestTransactionCommand() {
     clist = new Vector(); 
       alist = new Vector();
   }
   public void clearBuffer(Vector c, Vector a) {
     clist.removeAll(c);
       alist.removeAll(a); 
   }
   public Vector getClist() {
     return clist;
   }
   public Vector getAlist() {
     return alist;
   }
    public static void main(String[] args) {
       CommandArgument ca,ca2;
     TestTransactionCommand t = new TestTransactionCommand();
     ca = new CommandArgument();
     ca.setArgument(2,8);
     Vector myclist = t.getClist();
     Vector myalist = t.getAlist();
     myclist.addElement("Add"); myalist.addElement(ca);
     TransactionCommand tc = new TransactionCommand(myclist,myalist);
     CommandManager cm = new CommandManager(tc);       
                    cm.runCommands();
     t.clearBuffer(myclist,myalist);
     ca2 = new CommandArgument();
     ca2.setArgument(5,7);
     myclist = t.getClist();
     myalist = t.getAlist();
     myclist.addElement("Subtract"); myalist.addElement(ca2);
     myclist.addElement("Add"); myalist.addElement(ca2);
     TransactionCommand tc2 = new TransactionCommand(myclist,myalist);        
     CommandManager cm2 = new CommandManager(tc2);       
                    cm2.runCommands();
   }
 } 

可见,客户端的代码不依赖于任何的TransactionCommand子类,同时具有良好的可扩展性——需要增加新命令时可以定义子类并在CommandReceiver类提供新的命令处理方法。

总结

  • Command是整个模式的核心,Command的实现类是接收者和调用者的桥梁,接收者与命令实现类相关联,调用者发出请求到某个命令。
  • 具体的请求封装为命令对象,将命令存储在调用者对象中,使得不同动作的组合以及动作记录更加容易实现。
  • 增加新的命令时,不需要修改已有的代码。
  • 缺点:可能导致系统有过多的命令类以及复杂的关联关系。

关于作者: hejiani

(新浪微博:@jianihe

查看hejiani的更多文章 >>



相关文章

发表评论

Comment form

(*) 表示必填项

2 条评论

  1. 差分机 说道:

    TransactionCommand的角色和invoker的角色是不是有重合?

    Thumb up 0 Thumb down 0

跳到底部
返回顶部