zoukankan      html  css  js  c++  java
  • 状态模式、职责链模式——省去if-else的繁琐结构

    小时候写日记都是这么写的:上午七点起床,八点之前洗脸刷牙吃早饭,十二点之前好好上课,中午一点,吃午饭,下午两点到六点,上课,下课,找请假,明天妈妈要带我去姥姥家,九点之前,看动画片,九点钟,收拾去姥姥家的东西,十点以后,睡觉。

    我们把请假这块在充实一下:找班长请假,班长只能请半天,否则班长向老师申请,如果请假时间超过一周,老师要跟副年级主任请示,如果请假超出一个月,主任要跟年级正主任请示,然后被批准,或不被批准。

    如果用编程语言描述这两件事情,应该是这个样子的。

     

    [html] view plain copy
    1. public class DayWork  
    2. {  
    3.     private int hour;  
    4.       
    5.   
    6.     public void writeProgram()  
    7.     {  
    8.         if (hour 7)  
    9.         {  
    10.             System.out.println("当前时间:" + hour + "点 睡觉");  
    11.         }  
    12.         else if (hour = 7)  
    13.         {  
    14.             System.out.println("当前时间:" + hour + "洗脸刷牙吃早饭");  
    15.         }  
    16.         else if (hour 12)  
    17.         {  
    18.             System.out.println("当前时间:" + hour + "点 好好上课");  
    19.         }  
    20.         else if(hour=1)  
    21.         {  
    22.             System.out.println("当前时间:" + hour + "点 吃午饭");  
    23.               
    24.         }  
    25.         else if(hour<18)  
    26.         {  
    27.             System.out.println("当前时间:" + hour + "点 好好学习");  
    28.               
    29.         }  
    30.     }  
    31.   
    32.     public int getHour()  
    33.     {  
    34.         return hour;  
    35.     }  
    36.   
    37.     public void setHour(int hour)  
    38.     {  
    39.         this.hour = hour;  
    40.     }  
    41.   
    42.   
    43. }  
    44. //客户端代码  
    45. public class Main  
    46. {  
    47.     public static void main(String[] args)  
    48.     {  
    49.         DayWork work = new Work();  
    50.         work.setHour(9);  
    51.         work.writeProgram();  
    52.         work.setHour(10);  
    53.         work.writeProgram();  
    54.         work.setHour(12);  
    55.         work.writeProgram();  
    56.         work.setHour(13);  
    57.         work.writeProgram();  
    58.         work.setHour(14);  
    59.         work.writeProgram();  
    60.   
    61.     }  
    62. }  

     

    而请假的代码和这个差不多,if 请半天,班长请,else if 一周以内,老师请 else if 一个月以内 副主任请,else 超过一个月 主任请。

    可是,拿日记例子来看,过多的if分支并不是一件好事,它首先不满足开闭原则,一旦需要修改整个IF语句都需要修改,责任没有费解,也不符合单一职责原则,我们希望分解整个行为,把状态的判断逻辑转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化,这就是我们所说的状态模式。

    状态模式结构图:

     

    状态模式代码实现:

     

    [html] view plain copy
    1. //State类,抽象状态类,定义一个接口以封装与Context的一个特定状态相关的行为  
    2. public interface State  
    3. {  
    4.     public void handle(Context context);  
    5. }  
    6. //ConcreteState类,具体状态,每一个子类实现一个与Context的一个状态相关的行为。  
    7. public class ConcreteStateA implements State  
    8. {  
    9.     public void handle(Context context)  
    10.     {  
    11.         context.setState(new ConcreteStateB());  
    12.     }  
    13. }  
    14. public class ConcreteStateB implements State  
    15. {  
    16.     public void handle(Context context)  
    17.     {  
    18.         context.setState(new ConcreteStateA());  
    19.     }  
    20. }  
    21. //Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态  
    22. public class Context  
    23. {  
    24.     private State   state;  
    25.   
    26.     public Context(State state)  
    27.     {  
    28.         this.state = state;  
    29.     }  
    30.   
    31.     public void request()  
    32.     {  
    33.         state.handle(this);  
    34.     }  
    35.   
    36.     public State getState()  
    37.     {  
    38.         return state;  
    39.     }  
    40.   
    41.     public void setState(State state)  
    42.     {  
    43.         this.state = state;  
    44.         System.out.println("当前状态:" + state.getClass().getName());  
    45.     }  
    46. }  
    47. //客户端代码  
    48. public class Main  
    49. {  
    50.     public static void main(String[] args)  
    51.     {  
    52.         Context context = new Context(new ConcreteStateA());  
    53.   
    54.         context.request();  
    55.         context.request();  
    56.         context.request();  
    57.         context.request();  
    58.     }  
    59. }  

    状态模式将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。

     

    而且,状态模式把各种状态转移逻辑分布到State的子类之间,来减少相互间的依赖。

    请假问题也是一个很复杂的条件表达式,安理说用状态模式是可以使用的。

    但是,这里有一个问题,就是,如果班长请假了,用状态模式的道理讲,就是其他学生都请不了假了,也就是如果状态模式中任何一环缺失的话,这个事件都无法进行下去,怎么办?

    这就需要我们的职责链模式。

    结构图:

    代码实现:

     

    [html] view plain copy
      1. <pre name="code" class="html">// Chain of Responsibility pattern -- Structural example    
      2. using System;  
      3.   
      4. // "Handler"  
      5. abstract class Handler  
      6. {  
      7.   // Fields  
      8.   protected Handler successor;  
      9.    
      10.   // Methods  
      11.   public void SetSuccessor( Handler successor )  
      12.   {  
      13.     this.successor = successor;  
      14.   }  
      15.   abstract public void HandleRequest( int request );  
      16. }  
      17.   
      18. // "ConcreteHandler1"  
      19. class ConcreteHandler1 : Handler  
      20. {  
      21.   // Methods  
      22.   override public void HandleRequest( int request )  
      23.   {  
      24.     if( request >= 0 && request 10 )  
      25.       Console.WriteLine("{0} handled request {1}",  
      26.         this, request );  
      27.     else  
      28.       if( successor != null )  
      29.       successor.HandleRequest( request );  
      30.   }  
      31. }  
      32.   
      33. // "ConcreteHandler2"  
      34. class ConcreteHandler2 : Handler  
      35. {  
      36.   // Methods  
      37.   override public void HandleRequest( int request )  
      38.   {  
      39.     if( request >= 10 && request 20 )  
      40.       Console.WriteLine("{0} handled request {1}",  
      41.         this, request );  
      42.     else  
      43.       if( successor != null )  
      44.       successor.HandleRequest( request );  
      45.   }  
      46. }  
      47.   
      48. // "ConcreteHandler3"  
      49. class ConcreteHandler3 : Handler  
      50. {  
      51.   // Methods  
      52.   override public void HandleRequest( int request )  
      53.   {  
      54.     if( request >= 20 && request 30 )  
      55.       Console.WriteLine("{0} handled request {1}",  
      56.         this, request );  
      57.     else  
      58.       if( successor != null )  
      59.       successor.HandleRequest( request );  
      60.   }  
      61. }  
      62.   
      63. /**//// <summary>  
      64. /// Client test  
      65. /// </summary>  
      66. public class Client  
      67. {  
      68.   public static void Main( string[] args )  
      69.   {  
      70.     // Setup Chain of Responsibility  
      71.     Handler h1 = new ConcreteHandler1();  
      72.     Handler h2 = new ConcreteHandler2();  
      73.     Handler h3 = new ConcreteHandler3();  
      74.     h1.SetSuccessor(h2);  
      75.     h2.SetSuccessor(h3);  
      76.   
      77.     // Generate and process request  
      78.     int[] requests = { 2, 5, 14, 22, 18, 3, 27, 20 };  
      79.   
      80.     foreach( int request in requests )  
      81.       h1.HandleRequest( request );  
      82.   
      83.   }  

    职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象练成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

    从代码中我们可以看出,职责链模式的链式在客户端连接的,也就是说,如果我们请假,请假制度一旦改变,比如说我们不需要班长,或者是先请求老师后直接请求主任或者中间多了一个环节,都是很容易实现的,所以,职责链模式要比状态模式灵活很多。

     

    但是,这时候是不是有人要问,都可以解决If分支过多,是不是职责链模式比状态模式好呢,还是那句话,存在即合理,职责链模式虽然灵活,但是他过于灵活,我们在使用时需要确定下一个对象是谁,在多次设置的时候很容易出问题,所以,这时候用状态模式就比较好,就像我们记录一天的行为,事情已经发生,如果用职责链模式就显得画蛇添足了。

    从定义来看,状态模式是一个对象的内在状态发生改变(一个对象,相对比较稳定,处理完一个对象下一个对象的处理一般都已确定),而职责链模式是多个对象之间的改变(多个对象之间的话,就会出现某个对象不存在的现在,就像请假例子中的班长或者老师可能缺勤),这也说明他们两个模式处理的情况不同。

    其实,这两个设计模式最大的区别就状态模式是让各个状态对象自己知道其下一个处理的对象是谁,即在编译时便设定好了的;

    而职责链模式中的各个对象并不指定其下一个处理的对象到底是谁,只有在客户端才设定。用我们通俗的编程语言来说,就是

    状态模式:
      相当于If else if else;
      设计路线:各个State类的内部实现(相当于If,else If内的条件)
      执行时通过State调用Context方法来执行。
    职责链模式:
      相当于Swich case
      设计路线:客户设定,每个子类(case)的参数是下一个子类(case)。
      使用时,向链的第一个子类的执行方法传递参数就可以。

    就像对设计模式的总结,有的人采用的是状态模式,从头到尾,提前一定定义好下一个处理的对象是谁,而我采用的是职责链模式,随时都有可能调整链的顺序

  • 相关阅读:
    CentOS7安装iptables防火墙
    Linux下ntpdate时间同步
    linux下的时间及时区设置
    linux下的DNS
    sysctl
    ab -n -c
    VIM 中 查看{}是否闭合,按%跳转到下个闭合
    要删除共享的初始登陆名 cmd下输入net use * /delete
    case in esac ` for in do done ` while true / false
    read op case $op in
  • 原文地址:https://www.cnblogs.com/wyb628/p/7576186.html
Copyright © 2011-2022 走看看