轉(zhuǎn)帖|其它|編輯:郝浩|2010-11-15 14:25:46.000|閱讀 524 次
概述: 在上一章中,我們講述了有關(guān)業(yè)務(wù)層分層的一些知識(shí),下面我們就來(lái)看看,在具體的業(yè)務(wù)層的設(shè)計(jì)中,我們可以采用哪些模式可以將業(yè)務(wù)層設(shè)計(jì)的更加的靈活!
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
不管是GOF的23種設(shè)計(jì)模式,還是Flower的企業(yè)架構(gòu)模式,相信很多的朋友知道或者聽(tīng)說(shuō)過(guò)。在那些很經(jīng)典的書(shū)中,對(duì)模式都做了很精辟的解釋?zhuān)酒哪康脑谟诳纯催@些模式如何應(yīng)用在項(xiàng)目中的,并且給出一些代碼的例子,小洋也希望大家能夠真正的理解這些模式的思想,而不僅僅停留在代碼結(jié)構(gòu)和表面上。
在上一章中,我們講述了有關(guān)業(yè)務(wù)層分層的一些知識(shí),下面我們就來(lái)看看,在具體的業(yè)務(wù)層的設(shè)計(jì)中,我們可以采用哪些模式可以將業(yè)務(wù)層設(shè)計(jì)的更加的靈活!
架構(gòu)模式
首先我們就來(lái)看看,如何更加有效的組織業(yè)務(wù)規(guī)則。
Specification Pattern(需求規(guī)格模式)
這個(gè)模式的使用方法就是:把業(yè)務(wù)規(guī)則放在業(yè)務(wù)類(lèi)的外面,并且封裝成為一個(gè)個(gè)返回boolean值的算法。這些一個(gè)個(gè)的業(yè)務(wù)規(guī)則的算法不僅僅便于管理和維護(hù),并且還可以被重用,而且很方便的組織成為復(fù)雜的業(yè)務(wù)邏輯。
下面我們就來(lái)看一個(gè)以在線租DVD的公司的例子。例子很簡(jiǎn)單,場(chǎng)景也很簡(jiǎn)單:判斷一個(gè)用戶(hù)是否可以租更多的DVD。下面就是我們?cè)O(shè)計(jì)的一個(gè)基本的類(lèi)圖。(大家肯定覺(jué)得一上來(lái)就看類(lèi)圖有點(diǎn)突兀,沒(méi)有一步步的分析,其實(shí)我是想讓大家知道,所講的是個(gè)什么東西樣子,之后大家再慢慢的理解)
下面我們就開(kāi)始做這個(gè)事情:
1. 創(chuàng)建一個(gè)新的解決方案,命名為:ASPPatterns.Chap5.Specification,
2. 然后添加一個(gè)C#的類(lèi)庫(kù):ASPPatterns.Chap5. Specification.Model。
3. 在這個(gè)Model的類(lèi)庫(kù)中添加一個(gè)接口:ISpecification
cation.Solution.PNG" width="1005" border="0">
public interface ISpecification<T>
{
bool IsSatisfiedBy(T candidate);
}
上面的代碼,其實(shí)就是把一個(gè)個(gè)的業(yè)務(wù)規(guī)則抽象出來(lái)了。我們知道,在系統(tǒng)中,不管業(yè)務(wù)規(guī)則多么復(fù)雜,最后在進(jìn)行業(yè)務(wù)邏輯判定的時(shí)候,最后的結(jié)果還是“是否通過(guò)”。所以在這里就進(jìn)行了抽象。
因?yàn)槲覀兊睦邮且砸粋€(gè)在線租賃DVD為例子,用戶(hù)可以來(lái)租賃DVD,其中也是有一定的規(guī)則的,例如,如果用戶(hù)已經(jīng)租了5盤(pán)DVD,那么我們就會(huì)考慮,這個(gè)用戶(hù)時(shí)候還可以繼續(xù)租DVD。至于根據(jù)什么判斷:可能DVD公司規(guī)定一個(gè)人最多不能超過(guò)5盤(pán),或者DVD公司認(rèn)為某個(gè)用戶(hù)的信譽(yù)不好等等。
下面我們就來(lái)定義個(gè)具體的業(yè)務(wù)規(guī)則:HasReachedRentalThresholdSpecification
根據(jù)這個(gè)規(guī)則就決定一個(gè)用戶(hù)是否可以租DVD。
public class HasReachedRentalThresholdSpecification :
ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{
return candidate.NumberOfRentalsThisMonth >= 5;
}
}
這個(gè)規(guī)則定義出來(lái)后,我們就在業(yè)務(wù)類(lèi)中使用這個(gè)規(guī)則:
public class CustomerAccount
{
private ISpecification<CustomerAccount> _hasReachedRentalThreshold;
public CustomerAccount()
{
_hasReachedRentalThreshold =
new HasReachedRentalThresholdSpecification();
}
public decimal NumberOfRentalsThisMonth { get; set; }
public bool CanRent()
{
return !_hasReachedRentalThreshold.IsSatisfiedBy(this);
}
}
當(dāng)然,我們可以把更多的業(yè)務(wù)規(guī)則組合進(jìn)來(lái)。
這個(gè)例子到這里就完了,這個(gè)例子中只是簡(jiǎn)單的采用了Specifiction模式。但是實(shí)際的情況往往是沒(méi)有這個(gè)簡(jiǎn)單的,因?yàn)橐粋€(gè)業(yè)務(wù)邏輯往往要組合多個(gè)多個(gè)業(yè)務(wù)規(guī)則。下面我們就來(lái)進(jìn)一步的看:如果采用鏈?zhǔn)降慕Y(jié)構(gòu)來(lái)完成復(fù)雜的業(yè)務(wù)邏輯。
Composite Pattern(組合模式)
注:這個(gè)模式不屬于架構(gòu)模式,而且GOF模式的一種,這里列出來(lái)主要是為了配合之前的Specification模式的,大家不要在這里糾結(jié)這個(gè)問(wèn)題 J
Composite模式允許把一個(gè)集合對(duì)象當(dāng)做單個(gè)的對(duì)象來(lái)使用,而且我們還可以在這個(gè)所謂的”單個(gè)對(duì)象”中不斷的嵌套。采用這種模式,可以把對(duì)象的層級(jí)關(guān)系組合成為“樹(shù)形”的結(jié)構(gòu)!我個(gè)人喜歡把它稱(chēng)為“容器模式”。
其實(shí)這個(gè)模式在我們?cè)谄綍r(shí)的ASP.NET或者WinForm ,WPF中到處可見(jiàn)。例如一個(gè)Panel控件,可以在里面加入另一個(gè)Panel,然后在Panel中可以加入GroupBox,然后再GroupBox中還可以加入Button等控件。這就是.NET Framework設(shè)計(jì)中采用了Compiste模式的例子。
下面來(lái)看看Compiste模式的UML結(jié)構(gòu)圖:
在上面的圖中:
1. Component是一個(gè)抽象類(lèi),這個(gè)類(lèi)提供了一個(gè)Add方法,這個(gè)Add可以加入其他的Component.大家想想,這樣是否就可以很容易的實(shí)現(xiàn)鏈?zhǔn)降男Ч?/p>
2. Leaf就是一個(gè)繼承Component的具體類(lèi)。
看到上面圖,其實(shí)大家也可以想想在ASP.NET頁(yè)面的生命周期中到處都是這種例子:例如在ASP.NET頁(yè)面的Init事件中,因?yàn)镻age本身就是一個(gè)容器,這個(gè)容器里面包含了很多的其他的控件,如Panel,Button,而且Panel里面還是控件。那么在Init方法就會(huì)調(diào)用自己的子容器的Init方法,然后子容器在調(diào)用自己的子容器的Init方法,這樣就層層調(diào)用,直到最后調(diào)用到某個(gè)控件的Init的方法。這樣這個(gè)頁(yè)面的初始化就完成了。和上面的UML的結(jié)構(gòu)是一樣的。
下面我們還是來(lái)看一個(gè)例子吧。繼續(xù)之前的Specification模式的討論,看看如果結(jié)合則兩種模式來(lái)組織復(fù)雜的業(yè)務(wù)邏輯。
為了使得例子有點(diǎn)說(shuō)服力,我們把之前的業(yè)務(wù)稍微的變復(fù)雜一點(diǎn)點(diǎn):為了判定一個(gè)用戶(hù)是否可以租DVD,我們要進(jìn)行一系列的規(guī)則判定之后才能決定結(jié)果:
1. 用戶(hù)的賬號(hào)是否處于激活的狀態(tài)
2. 用戶(hù)之前是否還欠費(fèi)
3. 用戶(hù)租賃DVD的數(shù)量是否達(dá)到了規(guī)定的數(shù)量
下面首先總體來(lái)看看一些類(lèi)圖的結(jié)構(gòu):
不知道大家有沒(méi)有注意一點(diǎn):每次我在講述一個(gè)功能的時(shí)候,總是先讓大家看看總體的類(lèi)圖的設(shè)計(jì),然后再開(kāi)始一個(gè)個(gè)的講述。其實(shí)這樣做事有原因的。在之前的文章中,一直提到“設(shè)計(jì)Design”。就是說(shuō)在做一個(gè)功能之前,不是一下子就砸進(jìn)去編碼,而是首先把功能考慮清楚,然后從總體上考慮功能如何實(shí)現(xiàn),然后寫(xiě)出一些測(cè)試代碼,最后寫(xiě)出一些實(shí)現(xiàn)代碼的骨架。上面的類(lèi)圖其實(shí)就是一個(gè)骨架。
骨架出來(lái)了,下面就繼續(xù)開(kāi)始實(shí)現(xiàn),首先,因?yàn)橐紤]到用戶(hù)有了”是否處于激活狀態(tài)”,那么就在之前的CustomerAccoutn中加入屬性AccountActive.而且還要加入另外的屬性LateFees來(lái)保存用戶(hù)的欠費(fèi)的多少。
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);
}
}
那么隨著這個(gè)需求的變化,之前的CanRent方法也要改變了。
按照之前的Specification模式的例子,我們首先條件兩個(gè)類(lèi)來(lái)新增的封裝業(yè)務(wù)規(guī)則:
public class CustomerAccountStillActiveSpecification :
ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{
return candidate.AccountActive;
}
}
上面的代碼用來(lái)判斷用戶(hù)是否處于激活狀態(tài)
public class CustomerAccountHasLateFeesSpecification :
ISpecification<CustomerAccount>
{
public override bool IsSatisfiedBy(CustomerAccount candidate)
{
return candidate.LateFees > 0;
}
}
上面的代碼就判斷用戶(hù)是否欠費(fèi)
添加完了所有的業(yè)務(wù)規(guī)則之后,好戲就開(kāi)始了。
我們要把這些業(yè)務(wù)規(guī)則組合起來(lái),放在容器中,然后只要調(diào)用父容器的一個(gè)方法,規(guī)則驗(yàn)證就一層層進(jìn)行下去,就像我們之前舉的ASP.NET的Init事件一樣。
首先我們來(lái)添加一個(gè)表示容器的類(lèi):
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);
}
}
上面的代碼有些不明白的地方,沒(méi)什么,咱們耐心的往下面走。
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);
}
}
上面基礎(chǔ)代碼完成了,我們就開(kāi)始實(shí)現(xiàn)我們想要的鏈?zhǔn)降男Ч?/p>
我們修改之前的幾個(gè)規(guī)則,和接口的定義,如下:
public class HasReachedRentalThresholdSpecification :
CompositeSpecification<CustomerAccount>
{
…
}
public class CustomerAccountStillActiveSpecification :
CompositeSpecification<CustomerAccount>
{
…
}
public class CustomerAccountHasLateFeesSpecification :
CompositeSpecification<CustomerAccount>
{
…
}
漫長(zhǎng)的過(guò)程終于結(jié)束了,到了核心的部分,請(qǐng)看業(yè)務(wù)類(lèi)現(xiàn)在的定義:
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);
}
}
大家主要看看那個(gè) CanRent方法
下面我們就來(lái)講講這個(gè)方法。
customerAccountActive繼承自CompositeSpecification,而Add方法的定義如下:
public ISpecification<T> And(ISpecification<T> other)
{
return new AndSpecification<T>(this, other);
}
_customerAccountIsActive.And(_hasReachedRentalThreshold.Not())的結(jié)果就是使得customerAccountIsActive內(nèi)部包含了平行的兩條業(yè)務(wù)規(guī)則,結(jié)構(gòu)如下:
方法返回的結(jié)果還是一個(gè)實(shí)現(xiàn)了ISpecification的對(duì)象,只不過(guò)這個(gè)對(duì)象(我們稱(chēng)之為“容器A”)里面有兩個(gè)規(guī)則了。
然后這個(gè)保量?jī)蓚€(gè)業(yè)務(wù)規(guī)則的對(duì)象(容器A)再次調(diào)用Add方法,如下:
_customerAccountIsActive.And(_hasReachedRentalThreshold.Not()).
And(_customerAccountHasLateFees.Not());
此時(shí)相當(dāng)于把之前那個(gè)容器A作為一個(gè)單獨(dú)對(duì)象,再次調(diào)用Add方法,于是這個(gè)三個(gè)規(guī)則組合成為一個(gè)大的規(guī)則的容器:如下。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:博客園