走向ASP.NET架構設計——第五章:業務層模式,原則,實踐(后篇)
設計模式
本篇文章主要是接著討論的在業務層可以采用的或者常用的一些設計模式:
State模式
狀態模式允許一個對象在隨著它的狀態變化而改變它自身的一些行為。
在項目開發的過程中,有一些類,例如一個業務類常常是有自己的一些狀態的,而且還存在狀態之間的一些轉換,有些狀態之間是可以進行轉換的,有些狀態之間是不能轉換的。就拿一個汽車來舉例子,汽車有很多的狀態:靜止,啟動,前進,后退,停車。而且不能由”前進”狀態轉為“啟動”狀態。
很多朋友知道state模式的用法和結構,朋友們應該也清楚在狀態之間的轉換用swtich.. case的一些弊端。在項目中,很多時候就沒有”一定”,”非得”要用state模式來解決類似的問題,即使可以用state模式來解決。如果變化不大,switch.. case就夠用了。
下面還是來首先來看看使用state模式的一些例子。
還是采用電子商務為背景來舉例:每一個訂單都是有狀態的:New(新的),Shipped(已經發貨),Canceled(已取消)。我們知道一個新的訂單可以被變為”取消”的狀態,也可以成為”已發貨”狀態。但是訂單不能從”已發貨”狀態,變為”取消”的狀態。
下面就是例子中的類圖:

首先還是創建一個名為:ASPPatterns.Chap5.StatePattern的解決方案,添加一個名為:ASPPattern.Chap5.StatePattern.Model的類庫:
然后添加在Model的類庫中添加一個表示狀態接口的定義:IOrderState:
{
bool CanShip(Order Order);
void Ship(Order Order);
bool CanCancel(Order Order);
void Cancel(Order order);
OrderStatus Status { get; }
}
下面來定義個表示訂單狀態的枚舉:
{
New = 0,
Shipped = 1,
Canceled = 2
}
然后我們來看看,真正要進行狀態轉化的那個訂單類:
{
private IOrderState _orderState;
public Order(IOrderState baseState)
{
_orderState = baseState;
}
public int Id { get; set; }
public string Customer { get; set; }
public DateTime OrderedDate { 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)
{
_orderState = OrderState;
}
}
其實狀態模式一個最主要的思想就是:把狀態之間的轉換分離出來,把每一個狀態分解為一個個的狀態的類,然后這些狀態來負責如何在不同的狀態之間的轉換。也就是說,就本例而言,以前我們會在Order類中寫上如下的語句:
{
switch (Status)
{
case OrderStatus.New:
//.... do some things
break;
case OrderStatus.Shipped:
//.... do some things
break;
case OrderStatus.Canceled:
//.... do some things
break;
}
}
我們知道其實此時就是由Order類來控制了狀態之間的轉換。其實state模式的就是讓那些“狀態”變為一個“有思想”的狀態類,這些類自己來負責如何以及何時轉換到其他的狀態,這樣Order的“負擔”就減輕了。還有一個就是大家常常會”批判”:如何要添加一個新的狀態,那么ChangeStatus方法勢必會變化,因為這個方法控制了狀態之間的轉換,如果把狀態的轉換邏輯分離出去,最好做到添加或者減少狀態都不會影響現有的Order就更好了。
下面的講述有點直接,希望不熟悉state模式的朋友先“撐下去”J
1、當創建一個訂單的時。候,這個訂單的狀態肯定就是”New”(新的)。那么我們可能就傳入一個表示“New”狀態的類:OrderNewStatus:
new Order(OrderNewState newStatus)
OrderNewStatus把訂單的狀態標記為New
我們知道:訂單的狀態為New的時候,狀態可以向Canceled和Shipped轉換,那么OrderNewStatus的定義可能如下:
{
public bool CanShip(Order Order)
{
return true;
}
public void Ship(Order Order)
{
Order.Change(new OrderShippedState());
}
public OrderStatus Status
{
get { return OrderStatus.New; }
}
public bool CanCancel(Order Order)
{
return true;
}
public void Cancel(Order order)
{
order.Change(new OrderCanceledState());
}
public new Order(OrderNewState newStatus);
}
2、當新創建的訂單處理到一定的流程的時候,例如要發貨了,此時要更新訂單的狀態,此時我們只要調用Order的ship方法就行了。其實此時我們就是在調用NewOrderStatus類的Ship方法,這個類的ship方法知道怎么做:先判斷Order的是否可以向Shipped狀態轉換(調用CanShip方法),如果可以,那么就new一個OrderShippedStatus狀態的類,然后用這個類去替換原來的NewOrderStatus(調用Order類的Change方法),這樣,Order的狀態就是Shipped了,但是Order完全不用管狀態之間是如何變化的。
{
public bool CanShip(Order order)
{
return false;
}
public void Ship(Order order)
{
throw new NotImplementedException("You can't ship a shipped order!");
}
public OrderStatus Status
{
get { return OrderStatus.Shipped; }
}
public bool CanCancel(Order Order)
{
return false;
}
public void Cancel(Order order)
{
throw new NotImplementedException("You can't ship a shipped order!");
}
}
3、Canceled狀態也同理,我這里就不在贅述了。
{
public bool CanShip(Order Order)
{
return false;
}
public void Ship(Order Order)
{
throw new NotImplementedException("You can't ship a cancelled order!");
}
public OrderStatus Status
{
get { return OrderStatus.Canceled; }
}
public bool CanCancel(Order Order)
{
return false;
}
public void Cancel(Order order)
{
throw new NotImplementedException("You can't ship a cancelled order!");
}
}
我們用一個UML圖來結束state模式的講述:
Strategy模式
其實策略模式,我這里不打算作太多的講述,其實這種模式大家到處可見,我們常常在ASP.NET中常常提起的Provider模式,其實就是策略模式的一種實現。
大家看看結構圖,基本上就明白了:

在上述的圖中Context依賴一個IStrategy接口,我們可以決定讓Context使用IStrategy的任意的一個實現者:ConcreteStrategyA 或者ConcreteStrategyB。其實就是可以替換不同的實現者。可能大家在數據訪問層那塊有的體驗更加的明顯:定義一個IDataProvider,然后實現一個AdoDotNetProvider和一個LinqToSqlProvider。
架構模式
下面就來補充一些架構模式的知識,下文主要講述:Layer Supertype模式(超類模式)
超類模式就是定義一個基類,然后其他的所有的類都從這個類中繼承。對于業務層而言,在超類中可能會定義一些通用的業務規則和驗證方法,這樣就這些代碼被到處分散。也體現了繼承的一個好處。
下面我們就來看一個項目中的例子(電子商務為例),類結構如下:
大家可以看到,所有的業務類都是從EntityBase繼承的。
{
private T _Id;
private bool _idHasBeenSet = false;
private IList<string> _brokenRules = new List<string>();
public EntityBase()
{ }
public EntityBase(T Id)
{
this.Id = Id;
}
public T Id
{
get { return _Id; }
set
{
if (_idHasBeenSet)
ThrowExceptionIfOverwritingAnId();
_Id = value;
_idHasBeenSet = true;
}
}
private void ThrowExceptionIfOverwritingAnId()
{
throw new ApplicationException("You cannot change the id of an entity.");
}
public bool IsValid()
{
ClearCollectionOfBrokenRules();
CheckForBrokenRules();
return _brokenRules.Count() == 0;
}
protected abstract void CheckForBrokenRules();
private void ClearCollectionOfBrokenRules()
{
_brokenRules.Clear();
}
public IEnumerable<string> GetBrokenBusinessRules()
{
return _brokenRules;
}
protected void AddBrokenRule(string brokenRule)
{
_brokenRules.Add(brokenRule);
}
}
在這個超類中,提供了保存每個業務類唯一標識的邏輯,并且確保這個標識一旦設定就不會被改變。而且這個超類還提供一些簡單的驗證邏輯。
我們再來看看如何使用這個超類,下面定義了一個Customer的業務類:
{
public Customer() { }
public Customer(long Id)
: base(Id)
{ }
public string FirstName { get; set; }
public string LastName { get; set; }
protected override void CheckForBrokenRules()
{
if (String.IsNullOrEmpty(FirstName))
base.AddBrokenRule("You must supply a first name.");
if (String.IsNullOrEmpty(LastName))
base.AddBrokenRule("You must supply a last name.");
}
}
在這個類中,我們定義了唯一標識的類型:long,而且還定義了一些業務規則:FirstName, LastName不為空。至于如何調用這些驗證規則,在超類中已經實現了,此時業務類就“輕松”了很多—起碼不用再次寫那些相類似的代碼了,實現了一定程度上的代碼重用。
今天就講到這里了,不正確之處,還望朋友們指出,見諒!
多謝支持!