今天我們來將狀態模式,首先,我們來描述下麵一個場景: 一、案例: 在工作過程中,根據時間段的不同,我們工作的狀態也有所不同,下麵,我們用簡單的控制台應用程式,來實現一下這個場景。 客戶端 二、演繹 1、第一步演繹 看到上面用代碼描述的場景,對於我們學了好多設計模式的小伙伴來講,是不是顯得特別的挫,最 ...
今天我們來將狀態模式,首先,我們來描述下麵一個場景:
一、案例:
在工作過程中,根據時間段的不同,我們工作的狀態也有所不同,下麵,我們用簡單的控制台應用程式,來實現一下這個場景。
1 public static int Hour = 0;//鐘點 2 private static bool WorkFinished = false;//任務完成標記 3 4 public static void WriteProgram() 5 { 6 if (Hour < 12) 7 { 8 Console.WriteLine($"當前時間:{Hour}點,上午工作,狀態好"); 9 } 10 else if (Hour < 13) 11 { 12 Console.WriteLine($"當前時間:{Hour}點,午飯 犯困 午休"); 13 } 14 else if (Hour<17) 15 { 16 Console.WriteLine($"當前時間:{Hour}點,下午工作,狀態一般"); 17 } 18 else 19 { 20 if (WorkFinished) 21 { 22 Console.WriteLine($"當前時間:{Hour}點,下班回家了。"); 23 } 24 else 25 { 26 Console.WriteLine($"當前時間:{Hour}點,加班哦。"); 27 } 28 } 29 }
客戶端
1 public static void Main() 2 { 3 Hour = 9; 4 WriteProgram(); 5 Hour = 12; 6 WriteProgram(); 7 Hour = 13; 8 WriteProgram(); 9 Hour = 14; 10 WriteProgram(); 11 Hour = 17; 12 WriteProgram(); 13 Hour = 18; 14 WriteProgram(); 15 Console.ReadKey(); 16 }
二、演繹
1、第一步演繹
看到上面用代碼描述的場景,對於我們學了好多設計模式的小伙伴來講,是不是顯得特別的挫,最起碼,我們也要抽象出一個類來呀。好,下麵我們稍微改進一下代碼:
1 public class Work 2 { 3 private int hour; 4 5 public int Hour 6 { 7 get 8 { 9 return hour; 10 } 11 12 set 13 { 14 hour = value; 15 } 16 } 17 18 public bool Finish 19 { 20 get 21 { 22 return finish; 23 } 24 25 set 26 { 27 finish = value; 28 } 29 } 30 31 private bool finish = false; 32 33 public void WriteProgram() 34 { 35 if ( hour< 12) 36 { 37 Console.WriteLine($"當前時間:{hour}點,上午工作,狀態好"); 38 } 39 else if (hour < 13) 40 { 41 Console.WriteLine($"當前時間:{hour}點,午飯 犯困 午休"); 42 } 43 else if (hour < 17) 44 { 45 Console.WriteLine($"當前時間:{hour}點,下午工作,狀態一般"); 46 } 47 else 48 { 49 if (finish) 50 { 51 Console.WriteLine($"當前時間:{hour}點,下班回家了。"); 52 } 53 else 54 { 55 Console.WriteLine($"當前時間:{hour}點,加班哦。"); 56 } 57 } 58 } 59 }
客戶端:
1 public static void Main() 2 { 3 Work emergencyProjects = new Work(); 4 emergencyProjects.Hour = 9; 5 emergencyProjects.WriteProgram(); 6 emergencyProjects.Hour = 13; 7 emergencyProjects.WriteProgram(); 8 emergencyProjects.Hour = 16; 9 emergencyProjects.WriteProgram(); 10 emergencyProjects.Hour = 18; 11 emergencyProjects.WriteProgram(); 12 Console.ReadKey(); 13 }
上述代碼,我們將工作寫了一個Work類出來,讓客戶端來調用。
2、第二步演繹
那麼我們看看我們改過之後的代碼有什麼問題呢?請看WriteProgram()方法,現在是這些狀態分支,如果有更多的狀態分支,那麼這個方法將會很長,一個方法一大串代碼。
如果一個方法代碼過長,極有可能壞了味道。
我們的WriteProgram()方法中有很多判斷的分支,這意味著它的責任過大了。無論任何狀態,都需要通過它來改變,這其實是非常糟糕的。
面向對象設計其實就是希望做到代碼責任的分解,我們的Work類其實違背了“單一職責”的原則。
比如WriteProgram()這個方法,判斷這麼多,任何的增加和減少,都需要修改這個方法,一不留神,可能會將其他的判斷分支給修改了,這樣對整個代碼的維護風險是非常大的。
說白了,這也違反了“開放-封閉”原則。
通過上述的分析,我們可能會想到:將判斷的每一個分支都寫成一個一個的類,增加分支就相當於增加類,這樣也不會影響到其他的類。
理論想的很好,但是如何實現呢?那就是我們今天要講的狀態模式。
狀態模式:一個對象的內在狀態改變時,允許改變其行為,這個對象看起來像是改變了其類。
狀態模式主要解決:當控制一個對象狀態轉換的條件過於複雜時的情況,把狀態的判斷邏輯轉移到不同狀態的一系列類中去。可以把複雜的邏輯判斷簡化。當然,如果這個判斷非常的簡單,就沒有必要用“狀態模式”了。
好下麵我們來看一下狀態模式的結構
1 //封裝一個與Context的一個特定狀態相關的行為 2 abstract class State 3 { 4 public abstract void Handle(Context context); 5 } 6 //維護一個ConCreteState子類的實例,這個實例定義當前的狀態 7 class Context 8 { 9 private State state; 10 11 public Context(State state)//定義Context的初始狀態 12 { 13 this.State = state; 14 } 15 //可讀寫的狀態屬性,用於讀取當前狀態和設置當前狀態 16 internal State State 17 { 18 get 19 { 20 return state; 21 } 22 23 set 24 { 25 state = value; 26 Console.WriteLine($"當前狀態{state.GetType().Name}"); 27 } 28 } 29 //對請求做處理,並設置下一狀態 30 public void Request() 31 { 32 state.Handle(this); 33 } 34 } 35 /// <summary> 36 /// 具體的State類,實現一個與Context的一個狀態相關的行為 37 /// </summary> 38 class ConcreteStateA:State 39 { 40 public override void Handle(Context context) 41 { 42 //設置ConcreteStateA的下一狀態是ConcreteStateB 43 context.State = new ConcreteStateB(); 44 } 45 } 46 /// <summary> 47 /// 具體的State類,實現一個與Context的一個狀態相關的行為 48 /// </summary> 49 class ConcreteStateB : State 50 { 51 public override void Handle(Context context) 52 { 53 //設置ConcreteStateB的下一狀態是ConcreteStateA 54 context.State = new ConcreteStateA(); 55 } 56 }
客戶端調用:
1 public static void Main() 2 { 3 Context c = new Context(new ConcreteStateA());//設置Context的初始狀態為ConcreteStateA 4 //不斷的請求,同時更改狀態 5 c.Request(); 6 c.Request(); 7 c.Request(); 8 c.Request(); 9 c.Request(); 10 Console.ReadKey(); 11 }
狀態模式的好處就是將特定狀態的行為局部化,並將不同狀態的行為分割開來。
好,通過這個模式,我們來應用到我們的案例中來:
1 public abstract class State 2 { 3 public abstract void WrithProgram(Work w); 4 } 5 6 public class Work 7 { 8 private State current; 9 10 public Work() 11 { 12 current = new ForenoonState();//工作初始化為上午工作狀態,即上午9點 13 } 14 private double hour; 15 16 public double Hour//"鐘點"屬性,狀態轉換的依據 17 { 18 get 19 { 20 return hour; 21 } 22 23 set 24 { 25 hour = value; 26 } 27 } 28 29 public bool Finish 30 { 31 get 32 { 33 return finish; 34 } 35 36 set 37 { 38 finish = value; 39 } 40 } 41 42 private bool finish = false; 43 44 public bool TaskFinished//"任務完成"屬性,是否能下班的依據 45 { 46 get { return finish; } 47 set { finish = value; } 48 } 49 50 public void SetState(State s) 51 { 52 current = s; 53 } 54 55 public void WriteProgram() 56 { 57 current.WrithProgram(this); 58 } 59 } 60 //上午工作狀態 61 public class ForenoonState : State 62 { 63 public override void WrithProgram(Work w) 64 { 65 if (w.Hour < 12) 66 { 67 Console.WriteLine($"當前時間:{w.Hour}點,上午工作,狀態好"); 68 } 69 else//超過12點,則轉入中午工作狀態 70 { 71 w.SetState(new NoonState()); 72 w.WriteProgram(); 73 } 74 } 75 } 76 //中午工作狀態 77 public class NoonState:State 78 { 79 public override void WrithProgram(Work w) 80 { 81 if (w.Hour < 13) 82 { 83 Console.WriteLine($"當前時間:{w.Hour}點,午飯 犯困 休息"); 84 } 85 else//超過13點,則轉入下午工作狀態 86 { 87 w.SetState(new AfternoonState()); 88 w.WriteProgram(); 89 } 90 } 91 } 92 //下午工作狀態 93 public class AfternoonState:State 94 { 95 public override void WrithProgram(Work w) 96 { 97 if (w.Hour < 17) 98 { 99 Console.WriteLine($"當前時間:{w.Hour}點,下午工作,狀態一般"); 100 } 101 else//超過17點,則轉入旁晚工作狀態 102 { 103 w.SetState(new EveningState()); 104 w.WriteProgram(); 105 } 106 } 107 } 108 //傍晚工作狀態 109 public class EveningState:State 110 { 111 public override void WrithProgram(Work w) 112 { 113 if (w.TaskFinished)//完成任務則進入下班狀態 114 { 115 w.SetState(new RestState()); 116 w.WriteProgram(); 117 } 118 else 119 { 120 Console.WriteLine($"當前時間:{w.Hour}點,加班!"); 121 } 122 } 123 } 124 //下班狀態 125 public class RestState:State 126 { 127 public override void WrithProgram(Work w) 128 { 129 Console.WriteLine($"當前時間:{w.Hour}點,下班回家了"); 130 } 131 }
客戶端調用:
1 public static void Main() 2 { 3 Work emergencyProject = new Work(); 4 emergencyProject.Hour = 9; 5 emergencyProject.WriteProgram(); 6 emergencyProject.Hour = 10; 7 emergencyProject.WriteProgram(); 8 emergencyProject.Hour = 12; 9 emergencyProject.WriteProgram(); 10 emergencyProject.Hour = 13; 11 emergencyProject.WriteProgram(); 12 emergencyProject.Hour = 14; 13 emergencyProject.WriteProgram(); 14 emergencyProject.Hour = 17; 15 emergencyProject.TaskFinished = false; 16 emergencyProject.WriteProgram(); 17 Console.ReadKey(); 18 }
這就是狀態模式了。
今天狀態模式就講到這裡,下一篇我們會講 適配器模式
本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持。