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

作者: 小洋(燕洋天)  來源: 博客園  發布時間: 2010-11-18 22:05  閱讀: 867 次  推薦: 0   原文鏈接   [收藏]  
摘要:不管是GOF的23種設計模式,還是Flower的企業架構模式,相信很多的朋友知道或者聽說過。在那些很經典的書中,對模式都做了很精辟的解釋,本篇的目的在于看看這些模式如何應用在項目中的,并且給出一些代碼的例子,小洋也希望大家能夠真正的理解這些模式的思想,而不僅僅停留在代碼結構和表面上。

  在上一章中,我們講述了有關業務層分層的一些知識,下面我們就來看看,在具體的業務層的設計中,我們可以采用哪些模式可以將業務層設計的更加的靈活!

  架構模式

  首先我們就來看看,如何更加有效的組織業務規則。

  Specification Pattern(需求規格模式)

  這個模式的使用方法就是:把業務規則放在業務類的外面,并且封裝成為一個個返回boolean值的算法。這些一個個的業務規則的算法不僅僅便于管理和維護,并且還可以被重用,而且很方便的組織成為復雜的業務邏輯。

  下面我們就來看一個以在線租DVD的公司的例子。例子很簡單,場景也很簡單:判斷一個用戶是否可以租更多的DVD。下面就是我們設計的一個基本的類圖。(大家肯定覺得一上來就看類圖有點突兀,沒有一步步的分析,其實我是想讓大家知道,所講的是個什么東西樣子,之后大家再慢慢的理解)

  下面我們就開始做這個事情:

  1、創建一個新的解決方案,命名為:ASPPatterns.Chap5.Specification。

  2、然后添加一個C#的類庫:ASPPatterns.Chap5. Specification.Model。

  3、在這個Model的類庫中添加一個接口:ISpecification。

  
 
public interface ISpecification<T>
{
bool IsSatisfiedBy(T candidate);
}

  上面的代碼,其實就是把一個個的業務規則抽象出來了。我們知道,在系統中,不管業務規則多么復雜,最后在進行業務邏輯判定的時候,最后的結果還是“是否通過”。所以在這里就進行了抽象。

  因為我們的例子是以一個在線租賃DVD為例子,用戶可以來租賃DVD,其中也是有一定的規則的,例如,如果用戶已經租了5盤DVD,那么我們就會考慮,這個用戶時候還可以繼續租DVD。至于根據什么判斷:可能DVD公司規定一個人最多不能超過5盤,或者DVD公司認為某個用戶的信譽不好等等。

  下面我們就來定義個具體的業務規則:HasReachedRentalThresholdSpecification

  根據這個規則就決定一個用戶是否可以租DVD。   

 
public class HasReachedRentalThresholdSpecification : ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{

return candidate.NumberOfRentalsThisMonth >= 5;
}
}

  這個規則定義出來后,我們就在業務類中使用這個規則:

 
public class CustomerAccount
{

private ISpecification<CustomerAccount> _hasReachedRentalThreshold;

public CustomerAccount()
{
_hasReachedRentalThreshold
= new HasReachedRentalThresholdSpecification(); }

public decimal NumberOfRentalsThisMonth { get; set; }


public bool CanRent()
{

return !_hasReachedRentalThreshold.IsSatisfiedBy(this);
}
}

  當然,我們可以把更多的業務規則組合進來。

  這個例子到這里就完了,這個例子中只是簡單的采用了Specifiction模式。但是實際的情況往往是沒有這個簡單的,因為一個業務邏輯往往要組合多個多個業務規則。下面我們就來進一步的看:如果采用鏈式的結構來完成復雜的業務邏輯。

  Composite Pattern(組合模式)

  注:這個模式不屬于架構模式,而且GOF模式的一種,這里列出來主要是為了配合之前的Specification模式的,大家不要在這里糾結這個問題。

  Composite模式允許把一個集合對象當做單個的對象來使用,而且我們還可以在這個所謂的”單個對象”中不斷的嵌套。采用這種模式,可以把對象的層級關系組合成為“樹形”的結構!我個人喜歡把它稱為“容器模式”。

  其實這個模式在我們在平時的ASP.NET或者WinForm ,WPF中到處可見。例如一個Panel控件,可以在里面加入另一個Panel,然后在Panel中可以加入GroupBox,然后再GroupBox中還可以加入Button等控件。這就是.NET Framework設計中采用了Compiste模式的例子。

  下面來看看Compiste模式的UML結構圖:

  在上面的圖中:

  1. Component是一個抽象類,這個類提供了一個Add方法,這個Add可以加入其他的Component.大家想想,這樣是否就可以很容易的實現鏈式的效果。

  2. Leaf就是一個繼承Component的具體類。

  看到上面圖,其實大家也可以想想在ASP.NET頁面的生命周期中到處都是這種例子:例如在ASP.NET頁面的Init事件中,因為Page本身就是一個容器,這個容器里面包含了很多的其他的控件,如Panel,Button,而且Panel里面還是控件。那么在Init方法就會調用自己的子容器的Init方法,然后子容器在調用自己的子容器的Init方法,這樣就層層調用,直到最后調用到某個控件的Init的方法。這樣這個頁面的初始化就完成了。和上面的UML的結構是一樣的。

  下面我們還是來看一個例子吧。繼續之前的Specification模式的討論,看看如果結合則兩種模式來組織復雜的業務邏輯。

  為了使得例子有點說服力,我們把之前的業務稍微的變復雜一點點:為了判定一個用戶是否可以租DVD,我們要進行一系列的規則判定之后才能決定結果:

  1、用戶的賬號是否處于激活的狀態。

  2、用戶之前是否還欠費。

  3、用戶租賃DVD的數量是否達到了規定的數量。

  下面首先總體來看看一些類圖的結構:

  不知道大家有沒有注意一點:每次我在講述一個功能的時候,總是先讓大家看看總體的類圖的設計,然后再開始一個個的講述。其實這樣做事有原因的。在之前的文章中,一直提到“設計Design”。就是說在做一個功能之前,不是一下子就砸進去編碼,而是首先把功能考慮清楚,然后從總體上考慮功能如何實現,然后寫出一些測試代碼,最后寫出一些實現代碼的骨架。上面的類圖其實就是一個骨架。

  骨架出來了,下面就繼續開始實現,首先,因為要考慮到用戶有了”是否處于激活狀態”,那么就在之前的CustomerAccoutn中加入屬性AccountActive.而且還要加入另外的屬性LateFees來保存用戶的欠費的多少。

 
public class CustomerAccount
{

private ISpecification<CustomerAccount> _hasReachedRentalThreshold;

public CustomerAccount()
{
_hasReachedRentalThreshold
= new HasReachedRentalThresholdSpecification(); }

public decimal NumberOfRentalsThisMonth { get; set; }

public bool AccountActive { get; set; }

public decimal LateFees { get; set; }

public bool CanRent()
{

return !_hasReachedRentalThreshold.IsSatisfiedBy(this);
}
}

  那么隨著這個需求的變化,之前的CanRent方法也要改變了。

  按照之前的Specification模式的例子,我們首先條件兩個類來新增的封裝業務規則:

 
public class CustomerAccountStillActiveSpecification : ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{

return candidate.AccountActive;
}
}

  上面的代碼用來判斷用戶是否處于激活狀態:

 
public class CustomerAccountHasLateFeesSpecification : ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{

return candidate.LateFees > 0;
}
}

  上面的代碼就判斷用戶是否欠費,添加完了所有的業務規則之后,好戲就開始了。我們要把這些業務規則組合起來,放在容器中,然后只要調用父容器的一個方法,規則驗證就一層層進行下去,就像我們之前舉的ASP.NET的Init事件一樣。

  首先我們來添加一個表示容器的類:

 
public abstract class CompositeSpecification<T> : ISpecification<T>
{
public abstract bool IsSatisfiedBy(T candidate);

public ISpecification<T> And(ISpecification<T> other)
{

return new AndSpecification<T>(this, other);
}


public ISpecification<T> Not()
{

return new NotSpecification<T>(this);
}
}

  上面的代碼有些不明白的地方,沒什么,咱們耐心的往下面走。

 
public class AndSpecification<T> : CompositeSpecification<T>
{
private ISpecification<T> _leftSpecification;
private ISpecification<T> _rightSpecification;

public AndSpecification(ISpecification<T> leftSpecification, ISpecification<T> rightSpecification)
{
_leftSpecification
= leftSpecification;
_rightSpecification
= rightSpecification;
}


public override bool IsSatisfiedBy(T candidate)
{

return _leftSpecification.IsSatisfiedBy(candidate) && _rightSpecification.IsSatisfiedBy(candidate);
}
}

public class NotSpecification<T> : CompositeSpecification<T>
{
private ISpecification<T> _innerSpecification;

public NotSpecification(ISpecification<T> innerSpecification)
{
_innerSpecification
= innerSpecification;
}


public override bool IsSatisfiedBy(T candidate)
{

return !_innerSpecification.IsSatisfiedBy(candidate);
}
}

  上面基礎代碼完成了,我們就開始實現我們想要的鏈式的效果!我們修改之前的幾個規則,和接口的定義,如下:

 
public class HasReachedRentalThresholdSpecification :CompositeSpecification<CustomerAccount>
{

}


public class CustomerAccountStillActiveSpecification :CompositeSpecification<CustomerAccount>
{

}


public class CustomerAccountHasLateFeesSpecification :CompositeSpecification<CustomerAccount>
{

}

  漫長的過程終于結束了,到了核心的部分,請看業務類現在的定義:

 
public class CustomerAccount
{

private ISpecification<CustomerAccount> _hasReachedRentalThreshold;
private ISpecification<CustomerAccount> _customerAccountIsActive;
private ISpecification<CustomerAccount> _customerAccountHasLateFees;

public CustomerAccount()
{
_hasReachedRentalThreshold
= new HasReachedRentalThresholdSpecification();
_customerAccountIsActive
= new CustomerAccountStillActiveSpecification();
_customerAccountHasLateFees
= new CustomerAccountHasLateFeesSpecification();
}


public decimal NumberOfRentalsThisMonth { get; set; }

public bool AccountActive { get; set; }

public decimal LateFees { get; set; }

public bool CanRent()
{
ISpecification
<CustomerAccount> canRent = _customerAccountIsActive.And(_hasReachedRentalThreshold.Not()).And(_customerAccountHasLateFees.Not());

return canRent.IsSatisfiedBy(this);
}
}

  大家主要看看那個 CanRent方法,下面我們就來講講這個方法。customerAccountActive繼承自CompositeSpecification,而Add方法的定義如下:

 
public ISpecification<T> And(ISpecification<T> other)
{

return new AndSpecification<T>(this, other);
}

  _customerAccountIsActive.And(_hasReachedRentalThreshold.Not())的結果就是使得customerAccountIsActive內部包含了平行的兩條業務規則,結構如下:

  方法返回的結果還是一個實現了ISpecification的對象,只不過這個對象(我們稱之為“容器A”)里面有兩個規則了。

  然后這個保量兩個業務規則的對象(容器A)再次調用Add方法,如下:

 
_customerAccountIsActive.And(_hasReachedRentalThreshold.Not()).And(_customerAccountHasLateFees.Not());

  此時相當于把之前那個容器A作為一個單獨對象,再次調用Add方法,于是這個三個規則組合成為一個大的規則的容器:如下。

  今天就到這里,東西不多,大家多琢磨一下!

0
0
 
標簽:ASP.NET
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()