Java命令模式:设计模式的四人帮

本文介绍了一个Java设计模式:Java命令模式。这个模式很容易理解,分为四步:Command对象建立,Client对象,Invoker对象以及Receiver。

AD: <script src="http://www.51cto.com/js/article/keywords_ad_new.js"></script>

 

下面将对Java命令模式探讨一二。首先,让我们对命令模式进行一个简单的理解。

Command 命令模式

Intent:

Encapsulate a request as an object , thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

瞎谈:其实很好理解。命令模式,关心的就是命令(或者称为操作)。打个比方。在一个公司里面,整个运作就像一个系统。某个boss发布了一个命令,中层领导接到这个命令,然后指派给具体负责这个员工。整个流程很清晰吧。有一个需求,如何将这个流程固定下来,形成一个系统。我们只要抓住了重点:命令。将它抽取出来,其他的都迎刃而解了。抽取出命令,封装成一个独立的对象,实现了解耦。至于其他的,可以方便地扩展,不论这个命令是CEO,人事部,还是你爸提出来的。无论这个命令的执行者是张三还是王八。这个模式的产生,其实是哲学上的“抓住主要矛盾”。更多的例子,如其他作家举的“去路边吃烤肉和去烤肉店吃烤肉有什么不同”或者“美猴王大闹天宫中玉帝拍太白金星捉来猴子”。

正经:命令模式把一个请求或者操作封装到一个对象中。命令模式运行系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

Java命令模式本质是对命令的封装,从而把发出命令的责任和执行命令的责任分割开了,委派给不同的对象。通俗地说,我是老总,我只管发个命令,至于这个命令发给谁,谁执行,关我P事,我发钱请人不是为了给自己找麻烦。你是负责事情的员工,你的天职是做好上级交给你的任务,踏踏实实,不要知道太多,不要八卦,不要问太多了。

好处:

◆很容易构造一个命令队列

◆记录相关的命令日志

◆增加命令的状态,实现命令的撤销和重做

◆允许接受请求的一方决定是否可做

◆新的命令轻而易举可以加入其中

缺点:可能会有过多的具体命令类存在

实现:

也不难,第一步关键是建立Command对象。拥有点面向对象的思想,就知道把它先抽象,让继承它的对象去具体实现。Client对象是发布命令的。Invoker对象是传递命令的,就是跑腿的。Receiver是受气包,底层最累的程序员,负责干活吧。看看下面的类图就清晰了:

类图 

上面的类图,一开始我有一些疑问,不如Invoker为什么要存在。现在想通了,Client的职责只是发布命令,就不要给它增加传递命令的职责。因为客户有千千万万。在现实中,跑腿的人为什么要存在呢?因为领导之所以是领导,就是只发表命令,跑腿等差事就给跑腿之人去办吧。现实中,跑腿之人通常比做事之人混得要好些。因为他们之间面对的是领导,把做事之人功劳揽到自己身上。唉,没想到设计模式蕴含了如此深刻的道理,佩服“设计模式的四人帮Gof”

Java命令模式实现例子:

阎宏博士的书中举了很多生动有趣的例子,大家可以去查阅。比如一个Mp3。你按了一个播放键盘,就播放了。这就可以算是命令模式的一种。 你是Client ,按键是Invoker,mp3是Receiver,播放就是一个命令Command对象。

 

===================================================

 Client .java


package com.hegd.pattern.command;
public class Client {
   
   public static void main(String[] args) {
     
   /* 
   
    //首先客户找到美工组说,过来谈页面,并修改
     System.out.println("-------------客户要求删除一个页面-----------------");
     Group pg = new PageGroup();
     //找到需求组
     pg.find();
     
     //增加一个需求
     pg.delete();
     
     //要求变更计划
     pg.plan();
    
     */
  //定义我们的接头人
    Invoker xiaoSan = new Invoker();  //接头人就是我小三
    
   //客户要求增加一项需求
    System.out.println("-------------客户要求增加一项需求-----------------");
   //客户给我们下命令来
    Command command = new AddRequirementCommand();
    
   //接头人接收到命令
    xiaoSan.setCommand(command);
    
   //接头人执行命令
    xiaoSan.action();    
   }
 } 
  


  Invoker.java


package com.hegd.pattern.command;

 

 
public class Invoker {
   //什么命令
   private Command command;
   
   //客户发出命令
   public void setCommand(Command command){
     this.command = command;
   }
   
   //执行客户的命令
   public void action(){
     this.command.execute();
   }
}

 

 

 Command .java


package com.hegd.pattern.command;

 
public abstract class Command {
   
   //把三个组都定义好,子类可以直接使用
   protected RequirementGroup rg = new RequirementGroup();  //需求组
   protected PageGroup pg = new PageGroup();  //美工组
   protected CodeGroup cg = new CodeGroup();  //代码组;
   
   //只要一个方法,你要我做什么事情
   public abstract void execute();
     
 }

 

 AddRequirementCommand .java

package com.hegd.pattern.command;

 
public class AddRequirementCommand extends Command {
  //执行增加一项需求的命令
  public void execute() {
    //找到需求组
    super.rg.find();
    
    //增加一份需求
    super.rg.add();
    
    //给出计划
    super.rg.plan();
  }
 
}

 

DeletePageCommand .java

package com.hegd.pattern.command;

public class DeletePageCommand extends Command {
   
   //执行删除一个页面的命令
   public void execute() {
     //找到页面组
     super.pg.find();
     
     //删除一个页面
     super.rg.delete();
     
     //给出计划
     super.rg.plan();
   }
}

 

 

 Group .java
package com.hegd.pattern.command;
 
public abstract class Group {
 
   //甲乙双方分开办公,你要和那个组讨论,你首先要找到这个组
   public abstract void find();
   
   //被要求增加功能
   public abstract void add();
   
   //被要求删除功能
   public abstract void delete();
   
   //被要求修改功能
   public abstract void change();
   //被要求给出所有的变更计划
   public abstract void plan();   
 }

 

 RequirementGroup .java


package com.hegd.pattern.command;

 

 
public class RequirementGroup extends Group {
   
   //客户要求需求组过去和他们谈
   public void find() {
     System.out.println("找到需求组...");
   }
 
   //客户要求增加一项需求
   public void add() {
     System.out.println("客户要求增加一项需求...");
   }
 //客户要求修改一项需求
   public void change() {
      System.out.println("客户要求修改一项需求...");
    }
   //客户要求删除一项需求
   public void delete() {
      System.out.println("客户要求删除一项需求...");
    }
   //客户要求出变更计划
   public void plan() {
      System.out.println("客户要求需求变更计划...");
   }
  
}

 

 PageGroup .java


package com.hegd.pattern.command;

 

 
public class PageGroup extends Group {
   
   //首先这个美工组应该被找到吧,要不你跟谁谈?
   public void find() {
     System.out.println("找到美工组...");
   }
   
   //美工被要求增加一个页面
   public void add() {
     System.out.println("客户要求增加一个页面...");
   }
 
   //客户要求对现有界面做修改
   public void change() {
     System.out.println("客户要求修改一个页面...");
   }
   //甲方是老大,要求删除一些页面
   public void delete() {
     System.out.println("客户要求删除一个页面...");
   }
   
   //所有的增删改那要给出计划呀
   public void plan() {
     System.out.println("客户要求页面变更计划...");
   }
 } 

 

 CodeGroup .java


package com.hegd.pattern.command;

 

/**
 * 代码组的职责是实现业务逻辑,当然包括数据库设计了
 */
public class CodeGroup extends Group {
  
  //客户要求代码组过去和他们谈
  public void find() {
    System.out.println("找到代码组...");
  }
 
  
  //客户要求增加一项功能
  public void add() {
    System.out.println("客户要求增加一项功能...");
  }
//客户要求修改一项功能
  public void change() {
     System.out.println("客户要求修改一项功能...");
   }
  //客户要求删除一项功能
  public void delete() {
     System.out.println("客户要求删除一项功能...");
   }
  //客户要求出变更计划
  public void plan() {
     System.out.println("客户要求代码变更计划...");
   }
}

 

 

sing001 发表于 2011-02-26 13:45 回复此评论
好像对Command的封装不够。
既然client可以new Command的对象,就可以不通过Invoker而自己调用Command的excute了,岂不不安全了?
怎么办呢?

  • 艺术家 发表于 2011-03-14 12:38 回复此评论
    说的有道理,目前还没想出好办法!
  • 方小葱 发表于 2011-03-14 13:09 回复此评论

    引用来自“sing001”的评论

    好像对Command的封装不够。
    既然client可以new Command的对象,就可以不通过Invoker而自己调用Command的excute了,岂不不安全了?
    怎么办呢?

    我觉得不是封装不够@而是Command用的不对@
  • 艺术家 发表于 2011-03-22 11:46 回复此评论
    楼上正解啊,

    为此事,我特意请教了《设计模式之禅》作者,以下是作者给我的回复:
    client当然可以直接调用command类的execute方法,但问题是command是一堆命令的抽象,它的组合对client才是有意义的,那谁来管理和维护这个组合呢?就是Invoker,它不仅仅负责调用,通常还负责状态管理,比如命令池,撤销等。

    就是说直接调用就不是命令模式了,命令模式就是解决这些组合的分散问题,来统一管理。

  • 你可能感兴趣的:(Java命令)