State模式在對象內部狀態發生變化的時候,改變自身的行為,這通常是通過切換內部狀態對象實現的,對象將自身在各個狀態的行為推給了狀態對象,從而解開了行為與對象的依賴。
場景描述
在經典的訂單處理場景中,訂單其不同狀態的時候變現了不同的行為,具體內容如下:
- 假如訂單是一個新創建的訂單,那么它可以被寄送,也可以被取消;
- 假如訂單已經被寄送,那么它不可以被再次寄送,也不可以被取消;
- 假如訂單已經被取消,那么它不可以被寄送,也不可以被取消。
上述內容中詳細解釋了訂單狀態和對應行為的關系。
遇到問題
對邏輯的第一映像,通常是通過if-else或者switch子句,通過訂單內部的一個表示狀態的屬性,判斷出當前訂單是否可以寄送和取消。
可是這樣嚴重影響了程序代碼的課擴展性,想想一下,如果我需要添加一種叫做“已出庫”的狀態,此時訂單表現為可以被取消但是不可以再次申請寄送,那我們就需要在if-else子句中添加新的邏輯;又或者我們需要改變業務規則寄送的訂單可以在沒有完成前取消,那么我們又需要對訂單的實現代碼做邏輯更改,很明顯,這樣對擴展性來說是一個大問題。
所以,我們的解決方案是將訂單的行為推送到訂單狀態自身,這樣即使擴展再多的訂單狀態或者對狀態行為進行更改,也可以輕松應對,只對很少的類進行更改,並且不會牽涉到太多代碼邏輯。
解決問題走起
首先創建一個訂表示訂單狀態的枚舉OrderStatus
namespace Pattern.State
{
public enum OrderStatus
{
New=0,
Shipped=1,
Canceled=2
}
}
然后創建一個借口IOrderState,定義訂單的行為和保存訂單的狀態枚舉值
namespace Pattern.State
{
public interface IOrderState
{
bool CanShip(Order order);
void Ship(Order order);
bool CanCancel(Order order);
void Cancel(Order order);
OrderStatus Status { get; }
}
}
接下來就是最重要的Order類
namespace Pattern.State
{
public class Order
{
private IOrderState orderState;
public Order(IOrderState orderState)
{
this.orderState = orderState;
}
public int Id { get; set; }
public string CustomerName { get; set; }
public string Address { get; set; }
public OrderStatus Status()
{
return orderState.Status;
}
public bool CanCancel()
{
return orderState.CanCancel(this);
}
public void Cancel()
{
if (CanCancel())
{
orderState.Cancel(this);
}
}
public bool CanShip()
{
return orderState.CanShip(this);
}
public void Ship()
{
if (CanShip())
{
orderState.Ship(this);
}
}
internal void Change(IOrderState orderState)
{
this.orderState = orderState;
}
}
}
你可以看到,本來想象中的復雜了代碼邏輯沒有了,代碼變得更易懂易擴展,因為我們將這些行為轉到了IOrderState的子類中,單個子類只維護當前狀態下訂單的行為:
1.NewState
namespace Pattern.State
{
public class NewState:IOrderState
{
public bool CanShip(Order order)
{
//some logic here
return true;
}
public void Ship(Order order)
{
order.Change(new ShippedState());
}
public bool CanCancel(Order order)
{
return true;
}
public void Cancel(Order order)
{
order.Change(new CanceledState());
}
public OrderStatus Status
{
get { return OrderStatus.New; }
}
}
}
2.ShippedState
namespace Pattern.State
{
public class ShippedState:IOrderState
{
public bool CanShip(Order order)
{
return false;
}
public void Ship(Order order)
{
throw new InvalidOperationException();
}
public bool CanCancel(Order order)
{
return false;
}
public void Cancel(Order order)
{
throw new InvalidOperationException();
}
public OrderStatus Status
{
get { return OrderStatus.Shipped }
}
}
}
3.CanceledState
namespace Pattern.State
{
public class CanceledState:IOrderState
{
public bool CanShip(Order order)
{
return false;
}
public void Ship(Order order)
{
throw new InvalidOperationException();
}
public bool CanCancel(Order order)
{
return false;
}
public void Cancel(Order order)
{
throw new InvalidOperationException();
}
public OrderStatus Status
{
get { return OrderStatus.Canceled; }
}
}
}
最后我們創建一個OrderFactory
namespace Pattern.State
{
public static class OrderFactory
{
public static Order CreateOrder(string customerName, string address)
{
IOrderState orderState = new NewState();
Order order = new Order(orderState);
return order;
}
}
}
最后,通過一個控制台應用程序來測試一下:
namespace Pattern.Console
{
class Program
{
static void Main(string[] args)
{
Order order = OrderFactory.CreateOrder("小白哥哥", "天津市和平區");
if (order.CanShip())
{
System.Console.WriteLine("訂單當前可以寄送");
}
order.Ship();
if (!order.CanShip())
{
System.Console.WriteLine("訂單當前不可以寄送");
}
System.Console.ReadKey();
}
}
}
