本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8109100.html,記錄一下學習程序以備后續查用,
一、引言
今天我們要講行為型設計模式的第八個模式--職責鏈模式,讓我們看看現實生活中某公司采購流程的例子吧,理解起來可能更容易,某公司的規章制度
規定,采購原材料的總價在5萬之內,只需要經理級別的人批準即可;采購總價大于5萬小于10萬的則需要財務經理進行批準;總價大于10萬小于30萬的
需要總經理批準;總價大于30萬的則需要通過董事會會議討論決定,對于這樣一個需求,最直接的方法就是設計一個方法,該方法接受的引數是采購的總
價,然后在這個方法內對價格進行判斷,然后針對不同的條件交給不同級別的角色去處理,如果情況就是這樣,不變了,這樣做很好,沒問題,如果我們
又有新的條件要增加該怎么辦呢?我們不得不去修改原來設計的方法來再添加一個條件判斷,讓本已多重的if-else判斷陳述句更多了,這樣的設計顯然違背
了“開閉原則”,這時候,我們可以采用職責鏈模式來解決這樣的問題,
二、職責鏈模式介紹
職責鏈模式:英文名稱--Chain of Responsibility Pattern;分類--行為型,
2.1、動機(Motivate)
在軟體構建程序中,一個請求可能被多個物件處理,但是每個請求在運行時只能有一個接受者,如果顯示指定,將必不可少地帶來請求發送者與接受者
的緊耦合,如何使請求的發送者不需要指定具體的接受者,讓請求的接受者自己在運行時決定來處理請求,從而使兩者解耦,
2.2、意圖(Intent)
避免請求發送者與接收者耦合在一起,讓多個物件都有可能接受請求,將這些物件連接成一條鏈,并且沿著這條鏈傳遞請求,知道有物件處理它為止,
——《設計模式》GoF
2.3、結構圖(Structure)

2.4、模式的組成
可以看出,在職責鏈模式的結構圖有以下角色:
1)抽象處理者角色(Handler):抽象處理者定義了一個處理請求的介面,它一般設計為抽象類,由于不同的具體處理者處理請求的方式不同,因此在
其中定義了抽象請求處理方法,因為每一個處理者的下家還是一個處理者,因此在抽象處理者中定義了一個自型別的物件,作為其對下家的參考,通過該
參考,處理者可以連成一條鏈,
2)具體處理者角色(ConcreteHandler):具體處理者是抽象處理者的子類,它可以處理用戶請求,在具體處理者類中實作了抽象處理者中定義的抽象
處理方法,在處理請求之前需要進行判斷,看是否有相應的處理權限?如果可以處理請求就處理它,否則將請求轉發給后繼者,在具體處理者中可以訪問
鏈中下一個物件,以便請求的轉發,
2.5、職責鏈模式的具體實作
在現實生活中,職責鏈模式的例子也是很多的,例如:公司的請假流程就是一個很好的職責鏈模式的例子,如果請假半天,只要告訴本部門經理就可以
了;如果請假7天或者以上必須人事總監批準;如果請假15天以上,那就要經過總裁批準了,還有類似的例子就是采購的流程,其流程也是職責鏈模式很
好的體現,采購金額的不同,需要批準的人員也不同,下面就以采購的實體來說明職責鏈模式,實作代碼如下:
class Program { /// <summary> /// 采購請求 /// </summary> public sealed class PurchaseRequest { //金額 public double Amount { get; set; } //產品名字 public string ProductName { get; set; } public PurchaseRequest(double amount, string productName) { Amount = amount; ProductName = productName; } } /// <summary> /// 抽象審批人--相當于“抽象處理者角色” /// </summary> public abstract class Approver { //下一位審批人,由此形成一條鏈, public Approver NextApprover { get; set; } //審批人的名稱 public string Name { get; set; } public Approver(string name) { Name = name; } //處理請求 public abstract void ProcessRequest(PurchaseRequest request); } /// <summary> /// 部門經理--相當于“具體處理者角色” /// </summary> 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}批準了對原材料{1}的采購計劃,", Name, request.ProductName); } else if (NextApprover != null) { Console.WriteLine("部門經理{0}批準了對原材料{1}的采購計劃,", Name, request.ProductName); NextApprover.ProcessRequest(request); } } } /// <summary> /// 財務經理--相當于“具體處理者角色” /// </summary> 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}批準了對原材料{1}的采購計劃,", Name, request.ProductName); } else if (NextApprover != null) { Console.WriteLine("財務經理{0}批準了對原材料{1}的采購計劃,", Name, request.ProductName); NextApprover.ProcessRequest(request); } } } /// <summary> /// 總裁--相當于“具體處理者角色” /// </summary> 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}批準了對原材料{1}的采購計劃,", Name, request.ProductName); } else { Console.WriteLine("這個采購計劃的金額比較大,需要董事會會議討論才能決定,"); } } } static void Main(string[] args) { #region 職責鏈模式 PurchaseRequest requestDao = new PurchaseRequest(9000.0, "單刀5把"); PurchaseRequest requestHuaJi = new PurchaseRequest(40000.0, "10把方天畫戟"); PurchaseRequest requestJian = new PurchaseRequest(90000.0, "5把金絲龍鱗閃電劈"); Approver manager = new Manager("黃飛鴻"); Approver financial = new FinancialManager("黃麒英"); Approver ceo = new CEO("十三姨"); //設定職責鏈 manager.NextApprover = financial; financial.NextApprover = ceo; //處理請求 manager.ProcessRequest(requestDao); Console.WriteLine(); manager.ProcessRequest(requestHuaJi); Console.WriteLine(); manager.ProcessRequest(requestJian); Console.ReadLine(); #endregion } }View Code
運行結果如下:

三、職責鏈模式的實作要點
Chain of Responsibility模式的應用場合在于“一個請求可能有多個接受者,但是最后真正的接受者只有一個”,只有這時候請求發送者與接受者的耦合才
有可能出現“變化脆弱”的癥狀,職責鏈的目的就是將二者解耦,從而更好地應對變化,
應用了Chain of Responsibility模式后,物件的職責分派將更具靈活性,我們可以在運行時動態添加/修改請求的處理職責,
當我們要新增一個Handler處理請求,就不需再改原來的代碼了,遵從了開放封閉原則,這樣我們的程式就更賦予變化,更有變化的抵抗力,Handler類
本身繼承自BaseHandler型別,又包含了一個BaseHandler型別的物件,這點類似Decorator模式,
如果請求傳遞到職責鏈的末尾仍得不到處理,應該有一個合理的預設機制,這也是每一個接受物件的責任,而不是發出請求的物件的責任,
3.1、職責鏈模式的主要優點
1)降低耦合度:職責鏈模式使得一個物件無需知道是其它哪一個物件處理其請求,物件僅需知道該請求會被處理即可,接受者和發送者都沒有對方的明
確資訊,且鏈中的物件不需要知道鏈的結構,有客戶端負責鏈的創建,
2)可簡化物件的相互連接:接受者物件僅需維持一個指向其后繼者的參考,而不需維持它對所有的候選處理者的參考,
3)增強給物件指派職責的靈活性:在給物件分派職責時,職責鏈可以給我們帶來更多的靈活性,可以通過在運行時對該連進行動態的增加或修改處理一
個請求的職責,
4)增加新的請求處理類很方便:在系統中增加一個新的請求處理者無需修改原有系統的代碼,只需要在客戶端重新建鏈即可,從這一點看來是符合開閉
原則的,
3.2、職責鏈模式的主要缺點
1)在找到正確的處理物件之前,所有的條件判定都要執行一遍,當責任鏈過長時,可能會引起性能的問題,
2)可能導致某個請求不被處理,
3)客戶端需要組裝這個鏈條,耦合了客戶端和鏈條的組成結構,可以把這個在客戶端的組合動作提到外面,通過配置來做會更好點,
3.3、在下面的情況下可以考慮使用職責鏈模式
1)一個系統的審批需要多個物件才能完成處理的情況下,例如請假系統等,
2)代碼中存在多個if-else陳述句的情況下,此時可以考慮使用責任鏈模式來對代碼進行重構,
3)有多個物件可以處理同一個請求,具體哪個物件處理該請求在運行時刻自動確定,客戶端只需將請求提交到鏈上,無須關心請求的處理物件是誰以及
它是如何處理的,
4)不明確指定接受者的情況下,向多個物件中的一個提交一個請求,請求的發送者與請求者解耦,請求將沿著鏈進行傳遞,尋求回應的處理者,
5)可動態指定一組物件處理請求,客戶端可以動態創建職責鏈來處理請求,還可以動態改變鏈中處理者之間的先后次序,
四、.NET中職責鏈模式的實作
這個模式在.Net框架中的實作不多,個人覺得這個模式的使用場景更多的是在業務系統中才會有更大的用處,這種模式在處理UI的訊息時很常用,但實際
上Windows訊息回圈還是硬編碼的結構,主要是效率上的考慮,Windows訊息回圈是哪個物件有一個請求,則直接將請求送至處理函式的地址,如果鏈條上
的物件多了,而真正處理的函式在鏈條后部分,效率會很低下,因此我們在使用這種模式的時候更適合業務流程,即對性能要求不是特別高的情況更加常用,
五、總結
這個模式也是為了解耦,解耦請求的發送者和接受者,當有新的需求的時候更容易變化,讓我們的代碼更符合面向物件OO的設計,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/71355.html
標籤:C#
下一篇:C# 泛型
