State模式的經典應用場景:訂單處理(c#實現)


State模式在對象內部狀態發生變化的時候,改變自身的行為,這通常是通過切換內部狀態對象實現的,對象將自身在各個狀態的行為推給了狀態對象,從而解開了行為與對象的依賴。

場景描述

在經典的訂單處理場景中,訂單其不同狀態的時候變現了不同的行為,具體內容如下:

  1. 假如訂單是一個新創建的訂單,那么它可以被寄送,也可以被取消;
  2. 假如訂單已經被寄送,那么它不可以被再次寄送,也不可以被取消;
  3. 假如訂單已經被取消,那么它不可以被寄送,也不可以被取消。

上述內容中詳細解釋了訂單狀態和對應行為的關系。

遇到問題

對邏輯的第一映像,通常是通過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();
}
}
}

QQ截圖20140513165317


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM