狀態模式、職責鏈模式——省去if-else的繁瑣結構

小時候寫日記都是這麼寫的:上午七點起牀,八點以前洗臉刷牙吃早飯,十二點以前好好上課,中午一點,吃午餐,下午兩點到六點,上課,下課,找請假,明天媽媽要帶我去姥姥家,九點以前,看動畫片,九點鐘,收拾去姥姥家的東西,十點之後,睡覺。html

咱們把請假這塊在充實一下:找班長請假,班長只能請半天,不然班長向老師申請,若是請假時間超過一週,老師要跟副年級主任請示,若是請假超出一個月,主任要跟年級正主任請示,而後被批准,或不被批准。編程

若是用編程語言描述這兩件事情,應該是這個樣子的。設計模式

 

[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 超過一個月 主任請。app

但是,拿日記例子來看,過多的if分支並非一件好事,它首先不知足開閉原則,一旦須要修改整個IF語句都須要修改,責任沒有費解,也不符合單一職責原則,咱們但願分解整個行爲,把狀態的判斷邏輯轉移到表示不一樣狀態的一系列類當中,把複雜的判斷邏輯簡化,這就是咱們所說的狀態模式。編程語言

狀態模式結構圖:
ide

 

狀態模式代碼實現:oop

 

[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的子類之間,來減小相互間的依賴。動畫

請假問題也是一個很複雜的條件表達式,安理說用狀態模式是可使用的。this

可是,這裏有一個問題,就是,若是班長請假了,用狀態模式的道理講,就是其餘學生都請不了假了,也就是若是狀態模式中任何一環缺失的話,這個事件都沒法進行下去,怎麼辦?

這就須要咱們的職責鏈模式。

結構圖:

代碼實現:

 

[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)。
  使用時,向鏈的第一個子類的執行方法傳遞參數就能夠。

就像對設計模式的總結,有的人採用的是狀態模式,從頭至尾,提早必定定義好下一個處理的對象是誰,而我採用的是職責鏈模式,隨時都有可能調整鏈的順序

相關文章
相關標籤/搜索