天天看點

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

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

  前言:不管是GOF的23種設計模式,還是Flower的企業架構模式,相信很多的朋友知道或者聽說過。在那些很經典的書中,對模式都做了很精辟的解釋,本篇的目的在于看看這些模式如何應用在項目中的,并且給出一些代碼的例子,小洋也希望大家能夠真正的了解這些模式的思想,而不僅僅停留在代碼結構和表面上。

本篇的議題如下: 架構模式 設計模式 設計原則

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

  架構模式

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

  Specification Pattern(需求規格模式)

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

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

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

 public interface ISpecification<T>

    {

        bool IsSatisfiedBy(T entity); 

    }

    public class HasReachedMaxSpecification : ISpecification<Customer>

{

        public bool IsSatisfiedBy(Customer entity)

        {

            return entity.TotalRentNumber > 5;

        }

}

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

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

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

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

代碼

 public class HasReachedRentalThresholdSpecification : ISpecification<CustomerAccount> 

        public override bool IsSatisfiedBy(CustomerAccount candidate)

        {       

            return candidate.NumberOfRentalsThisMonth >= 5;        

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

        private ISpecification<Customer> specification = null;

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

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

  Composite Pattern(組合模式)

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

  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”。就是說在做一個功能之前,不是一下子就砸進去編碼,而是首先把功能考慮清楚,然後從總體上考慮功能如何實作,然後寫出一些測試代碼,最後寫出一些實作代碼的骨架。上面的類圖其實就是一個骨架。

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

    現在我們将例子進行擴充:為了判定一個使用者是否可以租DVD,我們要進行一系列的規則判定:

q 使用者的賬号是否處于激活的狀态。

q 使用者之前是否還欠費。

q 使用者租賃DVD的數量是否達到了規定的數量。

public class CustomerAccountStillActiveSpecification : ISpecification<CustomerAccount>  

            return candidate.AccountActive;

上面的代碼用來判斷使用者是否處于激活狀态

    public class CustomerAccountHasLateFeesSpecification : ISpecification<CustomerAccount>  

            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;

            return !_innerSpecification.IsSatisfiedBy(candidate);

上面基礎代碼完成了,我們就開始實作我們想要的鍊式的效果!

我們修改之前的幾個規則,和接口的定義,如下:

public class HasReachedRentalThresholdSpecification :CompositeSpecification<CustomerAccount>

        …

public class CustomerAccountStillActiveSpecification :CompositeSpecification<CustomerAccount>

       …

public class CustomerAccountHasLateFeesSpecification :CompositeSpecification<CustomerAccount>

      …

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

public class Customer

         private ISpecification<Customer> hasReachedRentalThreshold;

        private ISpecification<Customer> customerIsActive;

        private ISpecification<Customer> customerHasLateFees;

        public Customer()

          hasReachedRentalThreshold = new HasReachedMaxSpecification();

            customerIsActive = new CustomerAvtiveSpecification();

            customerHasLateFees = new CustomerHasLateFeesSpecification();

        public decimal TotalRentNumber { get; set; }

        public bool IsActive { get; set; }

        public decimal LateFees { get; set; }

        public bool CanRent()

            ISpecification<Customer>canRent =customerIsActive.And(hasReachedRentalThreshold.Not()).And(customerHasLateFees.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方法,于是這個三個規則組合成為一個大的規則的容器:如下。

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

本文轉自yanyangtian51CTO部落格,原文連結:http://blog.51cto.com/yanyangtian/423279 ,如需轉載請自行聯系原作者

繼續閱讀