需求:訂單有5個狀態(新訂單、已付款、已發貨、已完成、已取消)
正常狀態轉換:新訂單-》已付款-》已發貨-》已完成
除了已完成,其它都是可以取消的
已完成 、已取消:啥也不能做
對於這樣子的一個需求,似乎代碼很快就能給出,好,先看第一版
第一版:
using System; namespace DP.Observer.V4 { public class Order { public const int StatusNew = 1; public const int StatusPaid = 2; public const int StatusShipped = 3; public const int StatusComplete = 4; public const int StatusCancel = 0; private int _orderStatus; public string OrderCode { get; set; } public string UserName { get; set; } public int OrderStatus { get { return _orderStatus; } } public Order(string userName, string orderCode, int orderStatus) { UserName = userName; OrderCode = orderCode; _orderStatus = orderStatus; } public void Pay() { if (_orderStatus == StatusNew) { _orderStatus = StatusPaid; //do something Console.WriteLine("付款啦"); } } public void Ship() { if (_orderStatus == StatusPaid) { _orderStatus = StatusShipped; Console.WriteLine("發貨啦"); } } public void Complete() { if (_orderStatus == StatusShipped) { _orderStatus = StatusComplete; Console.WriteLine("訂單已完成"); } } public void Cancel() { if (_orderStatus == StatusNew || _orderStatus == StatusPaid || _orderStatus == StatusShipped) { _orderStatus = StatusCancel; Console.WriteLine("訂單已取消"); } Console.WriteLine("當前訂單狀態不能被取消"); } } }
如果需求不會變化,似乎這樣寫沒有任何問題
然而不幸的事情還是發生了,有一天老板就同我講,其實我們的訂單流程不僅僅是這樣的,也有可能是先發貨,完了收到貨再付款
那麼現在訂單的狀態轉換就有了兩種流程,這裡姑且把第一種叫做 ‘現款支付’,後者叫做 ‘貨到付款’
好,接下來就動手改代碼吧!
如果不知道狀態模式,我可能會這樣寫,首先給訂單加個類型標識它是 ‘現款支付’ 還是 ‘貨到付款’,然後
public const int TypeFirstPay = 1; public const int TypeFirstShip = 2; private int _orderType; public int OrderType { get { return _orderType; } }
public void Pay() { if (_orderType == TypeFirstPay) { if (_orderStatus == StatusNew) { _orderStatus = StatusPaid; //do something Console.WriteLine("付款啦"); } } else if (_orderType == TypeFirstShip) { if (_orderStatus == StatusPaid) { _orderStatus = StatusPaid; //do something Console.WriteLine("付款啦"); } } }
這裡僅僅以 pay() 方法做一個示例
實際上,這種寫法是很難維護和拓展的,舉個簡單的例子,訂單的類型可能不止兩個,而且訂單的狀態和流程也可能會變動,面對這種情況那些 if else 就足以讓人頭疼了
好,現在嘗試用狀態模式來改進這種 if else 的寫法,顯而易見的一點是,這裡訂單狀態的轉換流程是由 OrderType 決定的,所以在這裡 OrderType 才是狀態模式裡的狀態,並不是指訂單狀態哦!
(題外話,其實訂單狀態也可以寫狀態模式,之前的例子就是用訂單狀態寫的,但完了覺得沒有太大實際意義,純粹只是為了實現一個狀態模式,所以改用一個實際項目中遇到過的問題,就是這個訂單類型 OrderType)
訂單類:
using System; namespace DP.Observer.V5 { public class Order { public const int StatusNew = 1; public const int StatusPaid = 2; public const int StatusShipped = 3; public const int StatusComplete = 4; public const int StatusCancel = 0; public const int TypeFirstPay = 1; public const int TypeFirstShip = 2; private OrderType.OrderType _orderType; public string OrderCode { get; set; } public string UserName { get; set; } public int OrderStatus { get; set; } public int OrderType { get { return _orderType.GetOrderType(); } set { switch (value) { case TypeFirstPay: _orderType = new OrderType.FirstPay(); break; case TypeFirstShip: _orderType = new OrderType.FirstShip(); break; default: throw new Exception("error orderType"); } } } public Order(string userName, string orderCode, int orderStatus, int orderType) { UserName = userName; OrderCode = orderCode; OrderStatus = orderStatus; OrderType = orderType; } public void Pay() { _orderType.Pay(this); } public void Ship() { _orderType.Ship(this); } public void Complete() { _orderType.Complete(this); } public void Cancel() { _orderType.Cancel(this); } } }
狀態-父類:
using System; namespace DP.Observer.V5.OrderType { public abstract class OrderType { public abstract int GetOrderType(); public abstract void Pay(Order order); public abstract void Ship(Order order); public abstract void Complete(Order order); public virtual void Cancel(Order order) { { if (order.OrderStatus == Order.StatusNew || order.OrderStatus == Order.StatusPaid || order.OrderStatus == Order.StatusShipped) { order.OrderStatus = Order.StatusCancel; Console.WriteLine("訂單已取消"); } } } } }
狀態1:
using System; namespace DP.Observer.V5.OrderType { public class FirstPay : OrderType { public override int GetOrderType() { return Order.TypeFirstPay; } public override void Pay(Order order) { if (order.OrderStatus == Order.StatusNew) { order.OrderStatus = Order.StatusPaid; Console.WriteLine("付款啦"); } } public override void Ship(Order order) { if (order.OrderStatus == Order.StatusPaid) { order.OrderStatus = Order.StatusShipped; Console.WriteLine("發貨啦"); } } public override void Complete(Order order) { if (order.OrderStatus == Order.StatusShipped) { order.OrderStatus = Order.StatusComplete; Console.WriteLine("訂單已完成"); } } } }
狀態2:
using System; namespace DP.Observer.V5.OrderType { public class FirstShip : OrderType { public override int GetOrderType() { return Order.TypeFirstShip; } public override void Ship(Order order) { if (order.OrderStatus == Order.StatusNew) { order.OrderStatus = Order.StatusShipped; Console.WriteLine("發貨啦"); } } public override void Pay(Order order) { if (order.OrderStatus == Order.StatusShipped) { order.OrderStatus = Order.StatusPaid; Console.WriteLine("付款啦"); } } public override void Complete(Order order) { if (order.OrderStatus == Order.StatusPaid) { order.OrderStatus = Order.StatusComplete; Console.WriteLine("訂單已完成"); } } } }
雖然這種寫法看起來好像多出來許多類和代碼,但事實上業務邏輯的體現和代碼邏輯都會更清晰,對於維護和拓展來說也更加容易!