從注冊博客的那天起,我的職業生涯開始了漫長的求索過程。一點一滴的成長離不開博客園的滋潤。和很多人一樣,每天上班開始不是敲代碼而是打開博客園看看技術文章,每次遇到問題博客園走起。伴隨我1000多天的歲月,看看身邊人都在寫博客,我也拋開我悶騷的性格,套上豬一樣的臉皮,來這里分享下我工作中使用的設計模式,覺得有什么問題的可以留言或者直接Q我,大家在博客園的平台上一起成長。
言歸正傳,可能大家對狀態模式會有以下疑問:
什么是狀態模式?
什么時候用到狀態模式?
狀態模式有什么好處?
如何實現狀態模式?
接下來我通過我的親身親歷來回答以上幾個問題?
一:什么是狀態模式
狀態模式:顧名思義是對狀態變化的一個封裝。狀態模式就是當一個對象的內在狀態改變時允許改變其行為,這個對象看起來像是改變了其類[DP]。注:摘抄自大話設計模式
其實在工作中咱們或多或少都接觸到狀態變更的需求,只不過咱們對狀態的變化就是通過if else 或 swicth來判斷 比如 當狀態等於1的時候更改狀態為2,
狀態模式是通過子類繼承父類后,根據子類是否重寫父類狀態的虛函數來限制是否允許狀態變更到下一個狀態的過程。注:這樣可以防止狀態隨意改動,只有重寫父類虛函數的狀態子類才能變更到下一個指定的狀態。
二:什么時候用到狀態模式
當一個狀態的變更由上一個狀態決定時,應該使用狀態模式。
例如:我有一個訂單,訂單狀態有未提交,待審核,審核通過,審核失敗四個狀態,
訂單的變化應該是:點擊提交訂單時,訂單狀態由未提交變成待審核;點擊審核(待審核的訂單)訂單,可以變成審核通過或審核失敗。
此種情景使用狀態模式最為合適。
三:狀態模式的好處
1: 消除龐大的條件分支判斷
2: 減少狀態類之間的相互依賴
3:維護方便,我可以根據需求變更隨筆更改狀態的變更規則,而不用修改其他代碼。
4:體現開閉原則和單一原則。
5:安全。這點可能很多人會納悶,其實剛剛接觸的時候我也不太懂,在工作中用了以后領悟到了這點。這點我會在類圖和代碼中說明一下。
四:如何使用狀態模式
現在我做的一個功能是,貨主提交訂單的一個功能,貨主訂單有待提交(保存后未提交的)狀態,提交后需要管理員審核,審核通過更改狀態為審核通過,失敗更改為審核失敗。在這里我簡單列出來這幾個狀態用來說明狀態模式(因為我是做物流平台的,訂單狀態總共有30多種,所以我才想到了狀態模式,要不然30多種,你做swicth判斷要100行代碼吧,多個地方都這樣去判斷的話是不是就成了成堆的垃圾代碼了,萬一我狀態變更有新需求的話維護起來更是不言而喻的大坑啊)。
接下來我會把我用到的狀態模式寫的類圖和代碼一一列出來
狀態模式類圖如下:
類圖介紹:
StateHelper類:
屬性:UserId 當前操作的用戶id,OrderId 當前的訂單Id,State 當前的狀態值,_orderState 狀態父類 (通過里氏替換原則,實例化具體的子類)
方法:構造方法 OrderStateHelper 初始化屬性操作
SetState() 方法在構造方法里被調用,用來給_orderState 實例化具體的子類。此方法用privite來修飾
ChangState()方法是 改變訂單狀態為其他狀態 在調用狀態模式的時候調用
OrderState類:
UpdateState() 更新訂單狀態方法
PendingSubmission(),PendingAudit等是子類根據需要是否需要重寫的操作類
PendingSubmissionState(待提交狀態)類
繼承OrderState類,待提交狀態可以變成待審核狀態,所以重寫PendingAudit(待審核)方法
PendingAuditState(待審核狀態)類
繼承OrderState類,待審核狀態可以變成審核通過、審核不通過,所以重寫AuditNotPass(審核通過)和AuditPass(審核不通過)方法
AuditPassState(審核通過狀態)類和AuditNotPassState(審核不通過狀態)類
繼承OrderState類,由於審核通過和審核不通過狀態已經結束,所以不重寫任何方法
OrderStateEnum(訂單狀態枚舉)類
這里我重點說明一下 枚舉狀態的名字和類名去掉State后綴一樣,保持這個規則,方便用反射實現訂單狀態的變更
以下是源代碼
StateHelper類
namespace Solution.Logic.Order.OrderState
{
public class OrderStateHelper
{
/// <summary> /// 用戶Id /// </summary> public long UserId { get; set; } /// <summary> /// 訂單id /// </summary> public int OrderId { get; set; } /// <summary> /// 訂單父類 /// </summary> public OrderState _orderState { get; set; } public OrderStateHelper(long userId, string orderId, int state) { UserId = userId; OrderId = orderId; SetState(state); } #region 設置當前訂單狀態類(實例化子類) /// <summary> /// 設置當前訂單狀態 /// </summary> /// <param name="value">當前訂單狀態值</param> public void SetState(int value) { //設置當前枚舉值 _enumValue = EnumHelper.GetInstance<OrderStateEnum>(value); #region 通過命名空間+類名 實例化具體的子類 string adaptorName = this.GetType().Namespace + "." + _enumValue+ "State"; var adaptorType = Type.GetType(adaptorName); if (adaptorType != null) { _orderState = Activator.CreateInstance(Type.GetType(adaptorName), null) as OrderState; } #endregion } #endregion #region 改變訂單狀態為其他狀態 /// <summary> /// 改變訂單狀態為其他狀態 /// </summary> /// <param name="value">新的訂單狀態值</param> public bool ChangState(int value) { //設置當前枚舉值 _enumValue = EnumHelper.GetInstance<OrderStateEnum>(value); #region 通過反射方法名字符串動態調用方法 Type type = _orderState.GetType(); var method = type.GetMethod(_enumValue.ToString()); return (bool)method.Invoke(_orderState, new object[] { this }); #endregion } #endregion
}
}
下面是需要注意的一些規則:
OrderState類:
所有狀態類的父類,下面更新狀態用虛方法的好處時,子類根據需要去重寫更新狀態的方法,這樣防止惡意隨便更改狀態
namespace Solution.Logic.Order.OrderState { public abstract class OrderState { #region 更新狀態 /// <summary> /// 更新訂單狀態 /// </summary> /// <param name="helper">訂單狀態操作類</param> /// <param name="orderStateEnum">要更新的訂單狀態</param> protected virtual bool UpdateState(OrderStateHelper helper, OrderStateEnum orderStateEnum) { bool result = false; result = new OrderInfoBll().UpdateValue(helper.OrderId, OrderInfoTable.Status); return result; } #endregion #region 虛函數 #region 貨主 /// <summary> /// 待提交 /// </summary> /// <param name="helper">訂單狀態操作類</param> public virtual bool PendingSubmission(OrderStateHelper helper) { return false; } /// <summary> /// 待審核 /// </summary> /// <param name="helper">訂單狀態操作類</param> public virtual bool PendingAudit(OrderStateHelper helper) { return false; } /// <summary> /// 審核未通過 /// </summary> /// <param name="helper">訂單狀態操作類</param> public virtual bool AuditNotPass(OrderStateHelper helper) { return false; } /// <summary> /// 審核通過 /// </summary> /// <param name="helper">訂單狀態操作類</param> public virtual bool AuditNotPass(OrderStateHelper helper) { return false; }
PendingSubmissionState(待提交狀態)類
繼承OrderState類,待提交可以變成待審核狀態,所以重寫待審核狀態的方法
namespace Solution.Logic.Order.OrderState { /// <summary> /// 待提交狀態類 /// </summary> public class PendingSubmissionState:OrderState { /// <summary> /// 待提交可以變成待審核狀態,所以重寫待審核方法 /// </summary> /// <param name="helper"></param> /// <returns></returns> public override bool PendingAudit(OrderStateHelper helper) { return UpdateState(helper, OrderStateEnum.PendingAudit); } } }
PendingAuditState(待審核狀態)類
繼承OrderState類,待審核狀態可以變成審核通過、審核不通過,所以重寫AuditNotPass(審核通過)和AuditPass(審核不通過)方法
namespace Solution.Logic.Order.OrderState { /// <summary> /// 待審核狀態類 /// </summary> public class PendingAuditState : OrderState { /// <summary> /// 待審核可以變成審核未通過狀態,所以重寫審核未通過方法 /// </summary> /// <param name="helper">訂單狀態操作類</param> public override bool AuditNotPass(OrderStateHelper helper) { return UpdateState(helper, OrderStateEnum.AuditNotPass); } /// <summary> /// 待審核可以變成審核通過狀態,所以重寫審核通過方法 /// </summary> /// <param name="helper">訂單狀態操作類</param> /// <returns></returns> public override bool AuditPass(OrderStateHelper helper) { return UpdateState(helper, OrderStateEnum.AuditPass); }
AuditPassState(審核通過狀態)類
繼承OrderState類,由於審核通過狀態已經結束,所以不重寫任何方法
namespace Solution.Logic.Order.OrderState { /// <summary> /// 審核通過狀態類 /// </summary> public class AuditPassState : OrderState { } }
和AuditNotPassState(審核不通過狀態)類
繼承OrderState類,由於審核不通過狀態已經結束,所以不重寫任何方法
namespace Solution.Logic.Order.OrderState { /// <summary> /// 審核不通過狀態類 /// </summary> public class AuditNotPassState : OrderState { } }
OrderStateEnum(訂單狀態枚舉)類
這里我重點說明一下 枚舉狀態的名字和類名去掉State后綴一樣,保持這個規則,方便用反射實現訂單狀態的變更
namespace Solution.Logic.Order.OrderState { /// <summary> /// 訂單狀態枚舉 /// </summary> public enum OrderStateEnum { #region 訂單狀態枚舉 /// <summary> ///待提交 /// </summary> [Description("待提交")] PendingSubmission = 1, /// <summary> ///待審核 /// </summary> [Description("待審核")] PendingAudit = 3, /// <summary> ///審核通過 /// </summary> [Description("審核通過")] AuditPass = 5, /// <summary> ///審核未通過 /// </summary> [Description("審核未通過")] AuditNotPass = 7 #endregion 訂單狀態枚舉 } }
調用方法如下
var orderStateHelper = new OrderStateHelper(userId, orderId, state); var isChange = orderStateHelper.ChangState(newState);