.Net行為型設(shè)計(jì)模式之職責(zé)鏈模式(Chain of Responsibility)
一、動(dòng)機(jī)(Motivate)
在軟件構(gòu)建過(guò)程中,一個(gè)請(qǐng)求可能被多個(gè)對(duì)象處理,但是每個(gè)請(qǐng)求在運(yùn)行時(shí)只能有一個(gè)接受者,如果顯示指定,將必不可少地帶來(lái)請(qǐng)求發(fā)送者與接受者的緊耦合。如何使請(qǐng)求的發(fā)送者不需要指定具體的接受者,讓請(qǐng)求的接受者自己在運(yùn)行時(shí)決定來(lái)處理請(qǐng)求,從而使兩者解耦。
二、意圖(Intent)
避免請(qǐng)求發(fā)送者與接收者耦合在一起,讓多個(gè)對(duì)象都有可能接受請(qǐng)求,將這些對(duì)象連接成一條鏈,并且沿著這條鏈傳遞請(qǐng)求,知道有對(duì)象處理它為止。 ——《設(shè)計(jì)模式》GoF
三、結(jié)構(gòu)圖(Structure)

四、模式的組成
可以看出,在職責(zé)鏈模式的結(jié)構(gòu)圖有以下角色:
(1)、抽象處理者角色(Handler):抽象處理者定義了一個(gè)處理請(qǐng)求的接口,它一般設(shè)計(jì)為抽象類(lèi),由于不同的具體處理者處理請(qǐng)求的方式不同,因此在其中定義了抽象請(qǐng)求處理方法。因?yàn)槊恳粋€(gè)處理者的下家還是一個(gè)處理者,因此在抽象處理者中定義了一個(gè)自類(lèi)型的對(duì)象,作為其對(duì)下家的引用。通過(guò)該引用,處理者可以連成一條鏈。
(2)、具體處理者角色(ConcreteHandler):具體處理者是抽象處理者的子類(lèi),它可以處理用戶(hù)請(qǐng)求,在具體處理者類(lèi)中實(shí)現(xiàn)了抽象處理者中定義的抽象處理方法,在處理請(qǐng)求之前需要進(jìn)行判斷,看是否有相應(yīng)的處理權(quán)限,如果可以處理請(qǐng)求就處理它,否則將請(qǐng)求轉(zhuǎn)發(fā)給后繼者;在具體處理者中可以訪問(wèn)鏈中下一個(gè)對(duì)象,以便請(qǐng)求的轉(zhuǎn)發(fā)。
五、職責(zé)鏈模式的代碼實(shí)現(xiàn)
在現(xiàn)實(shí)生活中,職責(zé)鏈模式的例子也是很多的,例如:公司的請(qǐng)假流程就是一個(gè)很好的職責(zé)鏈模式的例子,如果請(qǐng)假半天,只要告訴本部門(mén)經(jīng)理就可以了;如果請(qǐng)假7天或者以上必須人事總監(jiān)批準(zhǔn);如果請(qǐng)假15天以上,那就要經(jīng)過(guò)總裁批準(zhǔn)了。還有類(lèi)似的例子就是采購(gòu)的流程,其流程也是職責(zé)鏈模式很好的體現(xiàn),采購(gòu)的金額不同,需要批準(zhǔn)的人員也不同,比如:部門(mén)采購(gòu)1萬(wàn)元的紙品,只要部門(mén)領(lǐng)導(dǎo)簽批就可以,如果要采購(gòu)大于1萬(wàn)小于5萬(wàn)的物品,那就需要財(cái)務(wù)經(jīng)理簽批了,如果采購(gòu)30萬(wàn)的原材料或者物品,那就需要總裁或者類(lèi)似角色才能審批了。接下來(lái)我們就以采購(gòu)的實(shí)例來(lái)說(shuō)明職責(zé)鏈模式。實(shí)現(xiàn)代碼如下:
static void Main(string[] args)
{
PurchaseRequest requestDao = new PurchaseRequest(8000.0, "單刀5把");
PurchaseRequest requestHuaJi = new PurchaseRequest(10000.0, "10把方天畫(huà)戟");
PurchaseRequest requestJian = new PurchaseRequest(80000.0, "5把金絲龍鱗閃電劈");
Approver manager = new Manager("黃飛鴻");
Approver financial = new FinancialManager("黃麒英");
Approver ceo = new CEO("十三姨");
// 設(shè)置職責(zé)鏈
manager.NextApprover = financial;
financial.NextApprover = ceo;
// 處理請(qǐng)求
manager.ProcessRequest(requestDao);
manager.ProcessRequest(requestHuaJi);
manager.ProcessRequest(requestJian);
}
// 采購(gòu)請(qǐng)求
public sealed class PurchaseRequest
{
// 金額
public double Amount { get; set; }
// 產(chǎn)品名字
public string ProductName { get; set; }
public PurchaseRequest(double amount, string productName)
{
Amount = amount;
ProductName = productName;
}
}
//抽象審批人,Handler---相當(dāng)于“抽象處理者角色”
public abstract class Approver
{
//下一位審批人,由此形成一條鏈
public Approver NextApprover { get; set; }
//審批人的名稱(chēng)
public string Name { get; set; }
public Approver(string name)
{
this.Name = name;
}
//處理請(qǐng)求
public abstract void ProcessRequest(PurchaseRequest request);
}
//部門(mén)經(jīng)理----相當(dāng)于“具體處理者角色” ConcreteHandler
public sealed class Manager : Approver
{
public Manager(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount <= 10000.0)
{
Console.WriteLine("{0} 部門(mén)經(jīng)理批準(zhǔn)了對(duì)原材料{1}的采購(gòu)計(jì)劃!", this.Name, request.ProductName);
}
else if (NextApprover != null)
{
NextApprover.ProcessRequest(request);
}
}
}
//財(cái)務(wù)經(jīng)理---相當(dāng)于“具體處理者角色”ConcreteHandler
public sealed class FinancialManager : Approver
{
public FinancialManager(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount > 10000.0 && request.Amount <= 50000.0)
{
Console.WriteLine("{0} 財(cái)務(wù)經(jīng)理批準(zhǔn)了對(duì)原材料{1}的采購(gòu)計(jì)劃!", this.Name, request.ProductName);
}
else if (NextApprover != null)
{
NextApprover.ProcessRequest(request);
}
}
}
//總裁---相當(dāng)于“具體處理者角色” ConcreteHandler
public sealed class CEO : Approver
{
public CEO(string name) : base(name) { }
public override void ProcessRequest(PurchaseRequest request)
{
if (request.Amount > 50000.0 && request.Amount < 300000.0)
{
Console.WriteLine("{0} 總裁批準(zhǔn)了對(duì)原材料 {1} 的采購(gòu)計(jì)劃!", this.Name, request.ProductName);
}
else
{
Console.WriteLine("這個(gè)采購(gòu)計(jì)劃的金額比較大,需要一次董事會(huì)會(huì)議討論才能決定!");
}
}
}六、職責(zé)鏈模式的實(shí)現(xiàn)要點(diǎn):
Chain of Responsibility模式的應(yīng)用場(chǎng)合在于“一個(gè)請(qǐng)求可能有多個(gè)接受者,但是最后真正的接受者只有一個(gè)”,只有這時(shí)候請(qǐng)求發(fā)送者與接受者的耦合才有可能出現(xiàn)“變化脆弱”的癥狀,職責(zé)鏈的目的就是將二者解耦,從而更好地應(yīng)對(duì)變化。
應(yīng)用了Chain of Responsibility模式后,對(duì)象的職責(zé)分派將更具靈活性。我們可以在運(yùn)行時(shí)動(dòng)態(tài)添加/修改請(qǐng)求的處理職責(zé)。
當(dāng)我們要新增一個(gè)DHandler處理請(qǐng)求,就不需再改原來(lái)的代碼了,遵從了開(kāi)放封閉原則。這樣我們的程序就更賦予變化,更有變化的抵抗力。Handler類(lèi)本身繼承自BaseHandler類(lèi)型,又包含了一個(gè)BaseHandler類(lèi)型的對(duì)象,這點(diǎn)類(lèi)似Decorator模式。
如果請(qǐng)求傳遞到職責(zé)鏈的末尾仍得不到處理,應(yīng)該有一個(gè)合理的缺省機(jī)制。這也是每一個(gè)接受對(duì)象的責(zé)任,而不是發(fā)出請(qǐng)求的對(duì)象的責(zé)任。
1、職責(zé)鏈模式的主要優(yōu)點(diǎn)有:
1】、降低耦合度:職責(zé)鏈模式使得一個(gè)對(duì)象無(wú)需知道是其他哪一個(gè)對(duì)象處理其請(qǐng)求。對(duì)象僅需知道該請(qǐng)求會(huì)被處理即可,接受者和發(fā)送者都沒(méi)有對(duì)方的明確信息,且鏈中的對(duì)象不需要知道鏈的結(jié)構(gòu),有客戶(hù)端負(fù)責(zé)鏈的創(chuàng)建。
2】、可簡(jiǎn)化對(duì)象的相互連接:接受者對(duì)象僅需維持一個(gè)指向其后繼者的引用,而不需維持它對(duì)所有的候選處理者的引用。
3】、增強(qiáng)給對(duì)象指派職責(zé)的靈活性:在給對(duì)象分派職責(zé)時(shí),職責(zé)鏈可以給我們帶來(lái)更多的靈活性??梢酝ㄟ^(guò)在運(yùn)行時(shí)對(duì)該連進(jìn)行動(dòng)態(tài)的增加或修改處理一個(gè)請(qǐng)求的職責(zé)。
4】、增加新的請(qǐng)求處理類(lèi)很方便:在系統(tǒng)中增加一個(gè)新的請(qǐng)求處理者無(wú)需修改原有系統(tǒng)的代碼,只需要在客戶(hù)端重新建鏈即可,從這一點(diǎn)看來(lái)是符合“開(kāi)閉原則”的。
2、職責(zé)鏈模式的主要缺點(diǎn)有:
1】、在找到正確的處理對(duì)象之前,所有的條件判定都要執(zhí)行一遍,當(dāng)責(zé)任鏈過(guò)長(zhǎng)時(shí),可能會(huì)引起性能的問(wèn)題。
2】、可能導(dǎo)致某個(gè)請(qǐng)求不被處理。
3】、客戶(hù)端需要組裝這個(gè)鏈條,耦合了客戶(hù)端和鏈條的組成結(jié)構(gòu),可以把這個(gè)在客戶(hù)端的組合動(dòng)作提到外面,通過(guò)配置來(lái)做,會(huì)更好點(diǎn)。
3、在下面的情況下可以考慮使用職責(zé)鏈模式:
1】、一個(gè)系統(tǒng)的審批需要多個(gè)對(duì)象才能完成處理的情況下,例如請(qǐng)假系統(tǒng)等。
2】、代碼中存在多個(gè)if-else語(yǔ)句的情況下,此時(shí)可以考慮使用責(zé)任鏈模式來(lái)對(duì)代碼進(jìn)行重構(gòu)
3】、有多個(gè)對(duì)象可以處理同一個(gè)請(qǐng)求,具體哪個(gè)對(duì)象處理該請(qǐng)求有運(yùn)行時(shí)刻自動(dòng)確定??蛻?hù)端只需將請(qǐng)求提交到鏈上,無(wú)須關(guān)心請(qǐng)求的處理對(duì)象是誰(shuí)以及它是如何處理的。
4】、不明確指定接受者的情況下,向多個(gè)對(duì)象中的一個(gè)提交一個(gè)請(qǐng)求。請(qǐng)求的發(fā)送者與請(qǐng)求者解耦,請(qǐng)求將沿著鏈進(jìn)行傳遞,尋求響應(yīng)的處理者。
5】、可動(dòng)態(tài)指定一組對(duì)象處理請(qǐng)求。客戶(hù)端可以動(dòng)態(tài)創(chuàng)建職責(zé)鏈來(lái)處理請(qǐng)求,還可以動(dòng)態(tài)改變鏈中處理者之間的先后次序
七、.NET 職責(zé)鏈模式的實(shí)現(xiàn)
這個(gè)模式在Net框架中的實(shí)現(xiàn)不多,我感覺(jué)這個(gè)模式的使用場(chǎng)景更多的是在業(yè)務(wù)系統(tǒng)總才會(huì)有更大的用處。這種模式在處理UI的消息時(shí)很常用,但實(shí)際上Windows消息循環(huán)還是硬編碼的結(jié)構(gòu)。因?yàn)樾噬系目紤],Windows消息循環(huán)是哪個(gè)對(duì)象有一個(gè)請(qǐng)求,則直接到達(dá)處理函數(shù)的地址。如果鏈條上的對(duì)象多了,而真正處理的函數(shù)在鏈條后部分,效率會(huì)很低下。因此我們?cè)谑褂眠@種模式的時(shí)候更適合業(yè)務(wù)流程,即對(duì)性能要求不是特別高的情況更加常用。
到此這篇關(guān)于.Net行為型設(shè)計(jì)模式之職責(zé)鏈模式(Chain of Responsibility)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- .Net行為型設(shè)計(jì)模式之備忘錄模式(Memento)
- .Net行為型設(shè)計(jì)模式之訪問(wèn)者模式(Visitor)
- .Net行為型設(shè)計(jì)模式之策略模式(Stragety)
- .Net行為型設(shè)計(jì)模式之狀態(tài)模式(State)
- .Net行為型設(shè)計(jì)模式之中介者模式(Mediator)
- .Net行為型設(shè)計(jì)模式之觀察者模式(Observer)
- .Net行為型設(shè)計(jì)模式之迭代器模式(Iterator)
- .Net行為型設(shè)計(jì)模式之命令模式(Command)
- .Net行為型設(shè)計(jì)模式之模板方法模式(Template?Method)
- .Net行為型設(shè)計(jì)模式之解釋器模式(Interpreter)
相關(guān)文章
asp.net網(wǎng)絡(luò)數(shù)據(jù)庫(kù)開(kāi)發(fā)實(shí)例精解 源文件
asp.net網(wǎng)絡(luò)數(shù)據(jù)庫(kù)開(kāi)發(fā)實(shí)例精解 源文件...2006-09-09
Blazor路由與頁(yè)面導(dǎo)航開(kāi)發(fā)介紹
這篇文章介紹了Blazor路由與頁(yè)面導(dǎo)航開(kāi)發(fā),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01
ASP.NET在VS2022中使用Dispose釋放資源實(shí)例
這篇文章介紹了ASP.NET在VS2022中使用Dispose釋放資源實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-11-11
ASP.NET MVC把數(shù)據(jù)庫(kù)中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字
這篇文章介紹了ASP.NET MVC把數(shù)據(jù)庫(kù)中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10
.NET設(shè)計(jì)模式之UML類(lèi)圖介紹
這篇文章介紹了.NET設(shè)計(jì)模式之UML類(lèi)圖,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
.NET?Core使用CZGL.SystemInfo庫(kù)獲取主機(jī)運(yùn)行資源
這篇文章介紹了.NET?Core使用CZGL.SystemInfo庫(kù)獲取主機(jī)運(yùn)行資源的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01
詳解ABP框架中的數(shù)據(jù)過(guò)濾器與數(shù)據(jù)傳輸對(duì)象的使用
ABP框架是一個(gè)基于ASP.NET的Web開(kāi)發(fā)框架,這里我們來(lái)詳解ABP框架中的數(shù)據(jù)過(guò)濾器與數(shù)據(jù)傳輸對(duì)象的使用,需要的朋友可以參考下2016-06-06
IIS部署ASP.NET?Core項(xiàng)目及常見(jiàn)問(wèn)題總結(jié)
這篇文章介紹了IIS部署ASP.NET?Core項(xiàng)目的方法及常見(jiàn)問(wèn)題總結(jié),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-01-01
學(xué)會(huì)區(qū)分Visual Studio 2005,Visual Studio 2005 Team System和MSDN P
學(xué)會(huì)區(qū)分Visual Studio 2005,Visual Studio 2005 Team System和MSDN Premium 訂閱的各個(gè)版本...2007-01-01

