走向.NET架構設計—第五章—業務層模式,原則,實踐(後篇)

weixin_34377065發表於2010-11-29
走向.NET架構設計第五章業務層模式,原則,實踐(後篇)
  前言:在上一篇文章中,講述了一些設計模式的使用,本篇首先接著介紹還沒有講完的一些設計模式,然後再講述一些架構模式中的超類模式,作為本篇的結尾。
 
本篇的議題如下:
架構模式        
設計模式        
設計原則
 
 
  設計模式
  本篇文章主要是接著討論的在業務層可以採用的或者常用的一些設計模式:
  Factory Method
  Decorator
  State
  Strategy
 
  State模式
  狀態模式允許一個物件在隨著它的狀態變化而改變它自身的一些行為。
    在專案開發的過程中,有一些類,例如一個業務類常常是有自己的一些狀態的,而且還存在狀態之間的一些轉換,有些狀態之間是可以進行轉換的,有些狀態之間是不能轉換的。就拿一個汽車來舉例子,汽車有很多的狀態:靜止,啟動,前進,後退,停車。而且不能由前進狀態轉為“啟動”狀態。
 
     很多朋友知道state模式的用法和結構,朋友們應該也清楚在狀態之間的轉換用swtich.. case的一些弊端。在專案中,很多時候就沒有一定”,”非得要用state模式來解決類似的問題,即使可以用state模式來解決。如果變化不大,switch.. case就夠用了。
 
     下面還是來首先來看看使用state模式的一些例子。
 
     還是採用電子商務為背景來舉例:每一個訂單都是有狀態的:New(新的),Shipped(已經發貨),Canceled(已取消)。我們知道一個新的訂單可以被變為取消的狀態,也可以成為已發貨狀態。但是訂單不能從已發貨狀態,變為取消的狀態。
 下面就是例子中的類圖:
  
 
  首先還是建立一個名為:ASPPatterns.Chap5.StatePattern的解決方案,新增一個名為:ASPPattern.Chap5.StatePattern.Model的類庫:
   
 
  然後新增在Model的類庫中新增一個表示狀態介面的定義:IOrderState:
  
ExpandedBlockStart.gif程式碼
 public interface IOrderState
    {        
        
bool CanShip(Order Order);
        
void Ship(Order Order);

        
bool CanCancel(Order Order);
        
void Cancel(Order order);

        OrderStatus Status { 
get; }
    }
 
 
 下面來定義個表示訂單狀態的列舉:
 
public enum OrderStatus
    {
       New 
= 0,
       Shipped 
= 1,
       Canceled 
= 2
    }
 
 
然後我們來看看,真正要進行狀態轉化的那個訂單類:
 
ExpandedBlockStart.gif程式碼
 public class Order
    {
        
private IOrderState _orderState;       

        
public Order(IOrderState baseState)
        {
            _orderState 
= baseState; 
        }

        
public int Id { getset; }

        
public string Customer { getset; }

        
public DateTime OrderedDate { getset; }
        
        
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類中寫上如下的語句:
 
ExpandedBlockStart.gif程式碼
 
public void ChangeStatus()
        {
            
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的時候,狀態可以向CanceledShipped轉換,那麼OrderNewStatus的定義可能如下:
 
ExpandedBlockStart.gif程式碼
 public class OrderNewState : IOrderState
    {
        
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.       當新建立的訂單處理到一定的流程的時候,例如要發貨了,此時要更新訂單的狀態,此時我們只要呼叫Ordership方法就行了。其實此時我們就是在呼叫NewOrderStatus類的Ship方法,這個類的ship方法知道怎麼做:先判斷Order的是否可以向Shipped狀態轉換(呼叫CanShip方法),如果可以,那麼就new一個OrderShippedStatus狀態的類,然後用這個類去替換原來的NewOrderStatus(呼叫Order類的Change方法),這樣,Order的狀態就是Shipped了,但是Order完全不用管狀態之間是如何變化的。
 
ExpandedBlockStart.gif程式碼
    public class OrderShippedState : IOrderState 
    {        
        
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狀態也同理,我這裡就不在贅述了。
 
ExpandedBlockStart.gif程式碼
public class OrderCanceledState : IOrderState 
    {        
        
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繼承的。
 
ExpandedBlockStart.gif程式碼
  public abstract class EntityBase<T>
    {        
        
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> GetBrokenBusine***ules()
        {
            
return _brokenRules;
        }

        
protected void AddBrokenRule(string brokenRule)
        {
            _brokenRules.Add(brokenRule);
        }
    }
 
在這個超類中,提供了儲存每個業務類唯一標識的邏輯,並且確保這個標識一旦設定就不會被改變。而且這個超類還提供一些簡單的驗證邏輯。
 
我們再來看看如何使用這個超類,下面定義了一個Customer的業務類:
 
ExpandedBlockStart.gif程式碼
  public class Customer : EntityBase<long>
    {
        
public Customer() { }

        
public Customer(long Id)
            : 
base(Id)
        { }

        
public string FirstName { getset; }
        
public string LastName { getset; }

        
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不為空。至於如何呼叫這些驗證規則,在超類中已經實現了,此時業務類就“輕鬆”了很多起碼不用再次寫那些相類似的程式碼了,實現了一定程度上的程式碼重用。
 
今天就講到這裡了,不正確之處,還望朋友們指出,見諒!
多謝支援!

相關文章