【愚公系列】2021年12月 二十三種設計模式(二十)-狀態模式
前言
設計模式(Design pattern)是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式于己于他人于系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。
提示:以下是本篇文章正文內容,下面案例可供參考
一、狀態模式(State Pattern)
狀態模式屬于行為型模式,它允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它的類。
狀態模式主要解決的是當控制一個對象狀態的條件表達式過于復雜時的情況。把狀態的判斷邏輯轉移到表示不同狀態的一系列類中,可以把復雜的判斷邏輯簡化。
二、使用步驟
角色
1、抽象狀態(State)
狀態模式的核心基類,它表示某種對象的不同狀態;
2、具體狀態(Concrete State)
實現抽象狀態的具體狀態類;
3、環境類(Context)
擁有狀態的具體對象,該對象會根據內部不同的對象有不同的行為。
示例
命名空間StatePattern中包含抽象狀態類State,代表水的3種狀態,0度及以下時為SolidState固體狀態,0到100度為LiquidState液體狀態,100度及以上時為GasState氣體狀態,并且不同的狀態可以改變水類Water中喝水Drink方法的行為。本案例嘗試以水的3種不同狀態來向大家闡述狀態模式在實際開發中的應用。
public class Water { public State State { get; set; } public double Temperature { get; set; } = 0; public Water() { State = new SolidState(); State.Water = this; } public Water Increase(int value) { State.Increase(value); return this; } public Water Reduce(int value) { State.Reduce(value); return this; } public Water Drink() { if (this.State is LiquidState) { Console.WriteLine("You can drink!"); } else { Console.WriteLine("You can not drink!"); } Console.WriteLine(Const.LINE_BREAK); return this; } }
Water水類充當環境類,公開一個狀態基類,并在內部維護一個溫度。Increase調用State的升溫,而Reduce調用State的降溫,最后的Drink方法會因為水的狀態的不同而擁有不同的行為。為了簡化邏輯,在本例中,只有當水為液體時才能被飲用。
public abstract partial class State { public static Water Water { get; set; } protected static string StateName { private get; set; } public void Increase(int value) { if (value == 0) return; if (value < 0) throw new ArgumentException(); OnStateChanging(); Water._temperature += value; ChangeState(); } public void Reduce(int value) { if (value == 0) return; if (value < 0) throw new ArgumentException(); if (Water._temperature - value <= Const.ABSOLUTE_ZERO) { throw new UnReachableException(); } OnStateChanging(); Water._temperature -= value; ChangeState(); } }
抽象狀態基類,首先公開一個水的引用,并在所有實現類中共享StateName狀態名,Increase為水升高一個溫度,而Reduce為水降溫。
public abstract partial class State { private void ChangeState() { if (Water._temperature <= 0) { Water.State = new SolidState(); } else if (Water._temperature > 0 && Water._temperature < 100) { Water.State = new LiquidState(); } else { Water.State = new GasState(); } OnStateChanged(); } protected virtual void OnStateChanging() { Console.WriteLine(Const.ON_STATE_CHANGING); Console.WriteLine( string.Format(Const.TEMPERATURE_INFO, Water._temperature, StateName)); } protected virtual void OnStateChanged() { Console.WriteLine(Const.ON_STATE_CHANGED); Console.WriteLine( string.Format(Const.TEMPERATURE_INFO, Water._temperature, StateName)); Console.WriteLine(Const.LINE_BREAK); } }
抽象狀態基類的第2部分(partial ),定義ChangeState方法以在改變溫度時更改狀態,另外定義OnStateChanging和OnStateChanged這2個受保護的虛方法以便提供“子類可以決定是否重寫相應的方法來影響父類”的這樣一個功能(OOP特性)。
public class SolidState : State { public SolidState() { StateName = "Solid"; } }
public class LiquidState : State { public LiquidState() { StateName = "Liquid"; } }
public class GasState : State { public GasState() { StateName = "Gas"; } }
水的3種狀態的具體實現類,SolidState固體狀態、LiquidState液體狀態和GasState氣體狀態,由于我們在狀態基類中封裝了較多的功能,所以此處的3個具體類都比較精簡,只在構造函數中更改共享的StateName狀態名稱字段。在實際開發過程中,應當盡可能的將具體的功能封裝在狀態實現類中。
public class Const { public const double ABSOLUTE_ZERO = -273.15; public const string LINE_BREAK = "--------------------------------------------------"; public const string ON_STATE_CHANGING = "OnStateChanging()"; public const string ON_STATE_CHANGED = "OnStateChanged()"; public const string TEMPERATURE_INFO = "The temperature is {0} °C" + " and state name is {1}!"; }
常量類,維護一些在本案例中經常使用到的字符串或數值。在實際開發過程中不應當有此類,應該將相應的常量放在具體要使用的類中。2017年,阿里發布《阿里巴巴Java開發手冊》,其中有一節提到此準則,所有使用面向對象編程語言的開發人員都應當遵從。
public class UnReachableException : Exception { public UnReachableException() : base("Absolute zero cannot be reached!") { } public UnReachableException(string message, Exception innerException) : base(message, innerException) { } }
絕對零度無法到達異常類UnReachableException,進行簡單的異常處理。
public class Program { private static Water _water = new Water(); public static void Main(string[] args) { try { _water.Increase(68) .Drink() .Increase(82) .Drink() .Reduce(90) .Drink() .Reduce(0) .Reduce(80) .Drink() .Reduce(300) .Drink(); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(Const.LINE_BREAK); } Console.ReadKey(); } }
以上是本案例的調用方代碼,升溫方法Increase、降溫方法Reduce和喝水方法Drink經過特別的處理以支持方法鏈。以下是這個案例的輸出結果:
OnStateChanging() The temperature is 0 °C and state name is Solid! OnStateChanged() The temperature is 68 °C and state name is Liquid! -------------------------------------------------- You can drink! -------------------------------------------------- OnStateChanging() The temperature is 68 °C and state name is Liquid! OnStateChanged() The temperature is 150 °C and state name is Gas! -------------------------------------------------- You can not drink! -------------------------------------------------- OnStateChanging() The temperature is 150 °C and state name is Gas! OnStateChanged() The temperature is 60 °C and state name is Liquid! -------------------------------------------------- You can drink! -------------------------------------------------- OnStateChanging() The temperature is 60 °C and state name is Liquid! OnStateChanged() The temperature is -20 °C and state name is Solid! -------------------------------------------------- You can not drink! -------------------------------------------------- Absolute zero cannot be reached! --------------------------------------------------
總結
優點
1、封裝了轉換規則;
2、枚舉可能的狀態,在枚舉狀態之前需要確定狀態種類;
3、將所有與某個狀態有關的行為放到一個類中,并且可以方便地增加新的狀態,只需要改變對象狀態即可改變對象的行為;
4、允許狀態轉換邏輯與狀態對象合成一體,而不是某一個巨大的條件語句塊;
5、可以讓多個環境對象共享一個狀態對象,從而減少系統中對象的個數。
缺點
1、狀態模式的使用必然會增加系統類和對象的個數;
2、狀態模式的結構與實現都較為復雜,如果使用不當將導致程序結構和代碼的混亂;
3、狀態模式對開閉原則的支持并不太好,對于可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的源代碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的源代碼。
使用場景
1、行為隨狀態改變而改變的場景;
2、條件、分支語句的代替者。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。