浅谈设计模式(职责链与命令模式)

来源:互联网 发布:室内设计网络教育 编辑:程序博客网 时间:2024/06/07 22:28

 菜菜又开始写设计模式的文章啦,今天和大家谈谈本人对职责链模式与命令模式的理解,并给出简易的demo(demo涉及到到菜菜,猫猫,涛涛,望见谅)。

为什么需要把这两个模式放在一起写呢,其实只是菜菜在学习这两个模式的时候,对于思想上的理解有些混淆而已,不过他们两者还是有一定关联的:

1.RC(Responsibility Chain)与Command模式都属于设计模式中的行为模式,所谓行为模式,即要描述对象或类的模式,而且要描述对象间的通讯模式

2.职责链模式使一个特定的请求接收对象对请求或命令的执行变得不确定,松耦合,而命令模式使得一个特定的对象与一个命令的执行变得明显和确定。 在Java中,异常捕获(try catch字句),就是用了职责链模式;而SWT的事件驱动机制则很好的运用了命令模式(菜菜的demo中也会设计到回调的实现)

好了,边上代码边和大家一起学习

职责链模式: 

涛涛经常在群里面问问题,假设,群里面就我和猫负责回答问题(当然,我们的群大家都很活跃),而且我一般只知道java方面的知识,猫比较精通.net。 于是乎,涛涛每次在群里面问问题的时候,都会标注是问题类型,问题内容(如:Java,java是什么?)。问题提出后,涛并不知道谁会回答,但是只要是符合我和猫胃口的,他总能得到满意答案。 于是乎,菜菜,猫,涛三人之间的职责链模式开始了

Code:
  1. public class ProgramRequest {  
  2. public static final int DOTNET_QUESTION = 1;  
  3. public static final int JAVA_QUESTION = 2;  
  4.   
  5. private int m_questionType;  
  6. private String m_content;  
  7.   
  8. public ProgramRequest(int type,String message){  
  9.     this.m_questionType = type ;  
  10.     this.m_content = message;  
  11. }  
  12. public int getType(){  
  13.     return m_questionType;  
  14. }  
  15. public String getContent(){  
  16.     return m_content;  
  17. }  
  18. }  

以上代码只是个负责标志涛涛提问请求的一个功能类,类似与javabean的功能,这里只做演示,所以属性较少。不多做解释。

Code:
  1. public abstract class IProgramHandler {  
  2.     protected String m_name;  
  3.     protected IProgramHandler m_nextHandler;  
  4.     public IProgramHandler(String name){  
  5.         this.m_name = name ;  
  6.     }  
  7.     public String getName(){  
  8.         return m_name;  
  9.     }  
  10.     public void setNextHandler(IProgramHandler handler){  
  11.         this.m_nextHandler = handler;  
  12.     }  
  13.     public IProgramHandler getNextHandler(){  
  14.         return m_nextHandler;  
  15.     }  
  16.     public abstract void  Handler(ProgramRequest request);  
  17. }  

以上代码是一个负责处理请求的接口,这里,菜菜用了抽象类来实现,各位也可以用Interface。 需要解释的是,该类里面有实际方法也有抽象方法,实际方法中getNextHandler是负责传递下一个任务处理者。 而handler这个抽象方法,就是菜菜和猫这两个类去负责实现。

 

菜菜处理类:

Code:
  1. public class CaiCaiHandler extends IProgramHandler {  
  2.   
  3.     public CaiCaiHandler(String name) {  
  4.         super(name);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.   
  8.     @Override  
  9.     public void Handler(ProgramRequest request) {  
  10.         // TODO Auto-generated method stub  
  11.         if(request.getType()==ProgramRequest.JAVA_QUESTION){  
  12.             System.out.println(m_name+"说: 你的问题是  "+"/""+request.getContent()+"/""+" 我来和你交流下吧");  
  13.         }  
  14.         else if(getNextHandler()!=null){      
  15.             getNextHandler().Handler(request);  
  16.         }  
  17.         else{  
  18.             System.out.println("目前菜菜和猫猫都不知道这个问题呀!");  
  19.         }  
  20.           
  21.     }  
  22.   
  23. }  

在菜菜的的实现方法 Handler中(写demo的时候,方法名写错了,不应该大写的哦,大家注意一下),根据传递的request参数,来判断是否是符合自己胃口的问题,如果是,则帮助涛涛处理;否则,像下一级传递,若有下一级,则重复之(递归么?)。否则,涛涛的问题无法得到解决。

 

猫类:

Code:
  1. public class MiaoMiaoHandler extends IProgramHandler {  
  2.   
  3.     public MiaoMiaoHandler(String name) {  
  4.         super(name);  
  5.         // TODO Auto-generated constructor stub  
  6.     }  
  7.   
  8.     @Override  
  9.     public void Handler(ProgramRequest request) {  
  10.         // TODO Auto-generated method stub  
  11.         if(request.getType()==ProgramRequest.DOTNET_QUESTION){  
  12.             System.out.println(m_name+"说: 你的问题是 "+"/""+request.getContent()+"/""+" 我来和你交流下吧");  
  13.         }  
  14.         else if(getNextHandler()!=null){      
  15.             getNextHandler().Handler(request);  
  16.         }  
  17.         else{  
  18.             System.out.println("目前菜菜和猫猫都不知道这个问题呀");  
  19.         }  
  20.     }  
  21.   
  22. }  

和上面一个子类一样,不多做解释。

下面是测试代码:

Code:
  1. public class TaoTaoRequest {  
  2.  public static void main(String args[]){  
  3.      IProgramHandler caicaiHandler = new CaiCaiHandler("菜菜");  
  4.      IProgramHandler miaomiaoHandler = new MiaoMiaoHandler("喵喵");  
  5.      ProgramRequest dotnetRequest =new ProgramRequest(1,"C#4.0是否支持了Duck Typing");  
  6.      ProgramRequest javaRequest =new ProgramRequest(2,"能否讲解一下jvm的原理");  
  7.      caicaiHandler.setNextHandler(miaomiaoHandler);  
  8.      caicaiHandler.Handler(dotnetRequest);  
  9.  }  
  10. }  

测试结果为:

Code:
  1. 喵喵说: 你的问题是 "C#4.0是否支持了Duck Typing" 我来和你交流下吧  

这里的测试代码,是将菜菜作为第一级接收者,猫作为第二级,涛涛现在问的问题是C#的,菜菜不懂,于是传递给猫,猫解决了。

 

看了上面的代码过程,相信大家会有和菜菜一样的疑惑:这样如果处理请求的人多了,而涛涛的问题没人能解决,或者刚好最后一个人能解决,岂不是要一直遍历到最后一步。产生了那么多没用的对象?  是的,职责链模式在这方面的确存在这样的 问题,可能产生很多对象,而且是没用的,导致内存的浪费。

但是,职责模式在一定程度上,给请求与处理两者之间解耦,请求者无需知道一个请求具体由哪个对象进行处理,只是将这个请求传递给职责链(这里,其实可以把菜菜这个类,当成一个链的首部),而且不同的处理者之间,只是通过一个引用进行关联,除非处理请求的算法改变,不然,这个模式很符合开闭原则。

 

下面开始讲Command模式,这个模式,菜菜写demo写了好久哦。 主要想把Java的回调方式也体现一下。 还是和以前一样,第二个模式直接上代码啦。

场景:一只会写程序的猫,一个想让猫改变写程序状态的主人。

Command接口:

Code:
  1. public interface ICommand {  
  2.     public void excute();  
  3.     public void undo();  
  4. }  

command模式一般接口里面就是一个方法excute(),但是,由于命令模式经常被用来处理redo和undo,所以也会添加此类方法,菜菜这里只演示简单的undo(撤销操作)。

具体实现类(这里,如果不用回调实现的话,是必须的,否则,可以在回调方法中实现)

Code:
  1. public class CatCommand implements ICommand {  
  2.     private Cat cat = null;  
  3.     String currentState ;  
  4.     public CatCommand(Cat cat){  
  5.         this.cat = cat;  
  6.     }  
  7.     @Override  
  8.     public void excute() {  
  9.         // TODO Auto-generated method stub  
  10.         currentState = cat.getState();  
  11.         System.out.println("猫的状态:"+cat.getState());  
  12.         cat.setSleep();  
  13.         System.out.println("执行excute后猫的状态:"+cat.getState());  
  14.     }  
  15.   
  16.     @Override  
  17.     public void undo() {  
  18.         // TODO Auto-generated method stub  
  19.         if(currentState.equals(Cat.ACTIVE)){  
  20.             cat.setActive();  
  21.              System.out.println("恢复猫的状态:"+cat.getState());  
  22.         }  
  23.         else if(currentState.equals(Cat.SLEEP)){  
  24.             cat.setSleep();  
  25.              System.out.println("恢复猫的状态:"+cat.getState());  
  26.         }  
  27.         else{  
  28.              cat.setProg();       
  29.              System.out.println("恢复猫的状态:"+cat.getState());  
  30.         }  
  31.           
  32.     }  
  33.   
  34.   
  35. }  

说一个undo方法,因为每次执行excute的时候,都会把当前状态保存在currentState这个变量中,所以执行undo方法时,即根据保存的状态,执行cat的相应方法,即可以达到撤销效果。 这里做演示用,只是一个String的属性,如果需要保存复杂的对象,可以考虑使用备忘录模式;如果需要保存大量的对象,则需要用栈来保存,然后一个个的执行undo。

上面代码中用到的cat类,用了饿汉单例模式:

Code:
  1. public class Cat {  
  2.  public static final String SLEEP="睡觉";  
  3.  public static final String ACTIVE="活蹦乱跳";  
  4.  public static final String PROGRAMING="写程序ing";  
  5.  private String catState;  
  6.    
  7.  private static Cat catInstance = new Cat();  
  8.    
  9.  private Cat(){  
  10.      catState =PROGRAMING;  
  11.  }  
  12.  public static Cat getInstance(){  
  13.      return catInstance;  
  14.  }  
  15.  public void setSleep(){  
  16.      catState = SLEEP;  
  17.  }  
  18.  public void setActive(){  
  19.      catState = ACTIVE;  
  20.  }  
  21.  public void setProg(){  
  22.      catState = PROGRAMING;  
  23.  }  
  24.  public String getState(){  
  25.      return catState;  
  26.  }  
  27. }  

Invoker类,命令对象的使用者:

Code:
  1. public class Invoker {  
  2.     /** 
  3.      * 用于回调方法 
  4.      */  
  5.     private ICommand command =null;  
  6.     public void excuteCommand(ICommand command){  
  7.      Cat cat = Cat.getInstance();  
  8.      System.out.println("猫的状态:"+cat.getState());  
  9.      command.excute();  
  10.      System.out.println("执行excute后猫的状态:"+cat.getState());  
  11.      command.undo();  
  12.      System.out.println("恢复猫的状态:"+cat.getState());  
  13.        
  14.  }  
  15.  /** 
  16.   * 用于非回调方法 
  17.   */  
  18.  public void excuteCatCommand(){  
  19.      command.excute();  
  20.      command.undo();  
  21.  }  
  22.  public void setCommand(ICommand cmd){  
  23.      this.command = cmd;  
  24.  }  
  25. }  

如果需要用回调方法,那么是不需要指定传递command接口的实现类对象的;如果不用回调,只需要先调用setCommand方法,制定command的实现类对象,然后调用实现类对象的excute和undo方法。

客户端调用类:

Code:
  1. public class Client {  
  2. public static void main(String args[]){  
  3.     /** 
  4.      * 回调方式实现 
  5.      */  
  6. //  Invoker invoker = new Invoker();  
  7. //  invoker.excuteCommand(new ICommand() {  
  8. //        
  9. //      String currentState;  
  10. //      Cat cat = Cat.getInstance();  
  11. //      @Override  
  12. //      public void excute() {  
  13. //          // TODO Auto-generated method stub  
  14. //          currentState =cat.getState();  
  15. //          cat.setActive();  
  16. //      }  
  17. //  
  18. //      @Override  
  19. //      public void undo() {  
  20. //          // TODO Auto-generated method stub  
  21. //          if(currentState.equals(Cat.ACTIVE)){  
  22. //              cat.setActive();  
  23. //          }  
  24. //          else if(currentState.equals(Cat.SLEEP)){  
  25. //              cat.setSleep();  
  26. //          }  
  27. //          else{  
  28. //              cat.setProg();        
  29. //          }  
  30. //      }  
  31. //  });  
  32.     /** 
  33.      * 非回调方式 
  34.      */  
  35.     Invoker  invoker = new Invoker();  
  36.     ICommand catCommand = new CatCommand(Cat.getInstance());  
  37.     invoker.setCommand(catCommand);  
  38.     invoker.excuteCatCommand();  
  39.       
  40. }  
  41. }  

注释代码为Java的回调实现,如果用这个方式,上面的CatCommand类就不需要,invoker类中,也可以直接执行第一个方法。

(顺带着扯一下,java中的回调机制,是靠传递接口引用来实现,和C不一样的哦)。

测试结果为:

Code:
  1. 猫的状态:写程序ing  
  2. 执行excute后猫的状态:睡觉  
  3. 恢复猫的状态:写程序ing  

好了。又写了篇文章,这段时间只发表了一个学习python的小感触,发现自己写文章又犯糊涂了,如果大家看着觉得迷糊的话,见谅啊,可以和菜菜单独交流。 欢迎交流,拍砖。