本文中,我們將介紹設計模式中的行為型模式職責鏈模式,職責鏈模式的結果看上去很簡單,但是也很復雜。首先我們來了解下現實生活中的鏈子,如下圖:
了解職責鏈之前我們先來了解下職責鏈模式的模式動機:
模式動機
職責鏈可以是一條直線、一個環或者一個樹形結構,最常見的職責鏈是直線型,即沿着一條單向的鏈來傳遞請求。
鏈上的每一個對象都是請求處理者,職責鏈模式可以將請求的處理者組織成一條鏈,並使請求沿着鏈傳遞,由鏈上的處理者對請求進行相應的處理,
客戶端無須關心請求的處理細節以及請求的傳遞,只需將請求發送到鏈上即可,將請求的發送者和請求的處理者解耦。這就是職責鏈模式的模式動機。
模式定義
職責鏈模式(Chain of Responsibility Pattern):避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,
並且沿着這條鏈傳遞請求,直到有對象處理它為止。由於英文翻譯的不同,職責鏈模式又稱為責任鏈模式,它是一種對象行為型模式。
模式結構
職責鏈模式包含如下角色:
•
Handler: 抽象處理者:定義出一個處理請求的接口。如果需要,接口可以定義出一個方法,以設定和返回對下家的引用。這個角色通常由一個抽象類或接口實現。
•
ConcreteHandler: 具體處理者:具體處理者接到請求后,可以選擇將請求處理掉,或者將請求傳給下家。由於具體處理者持有對下家的引用,因此,如果需要,具體處理者可以訪問下家。
•
Client: 客戶端
下面我們通過具體的例子來分析說明如何使用職責鏈模式:
審批假條
•某OA系統需要提供一個假條審批的模塊,如果員工請假天數小於10天,TeamLeader可以審批該假條;如果員工請假天數大於等於10天,小於20天,項目經理可以審批;如果員工請假天數大於等於20天,小於30天,HR可以審批。大於30天無法審批!
在解決方案中新建如下角色:
定義抽象處理者:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using ChainOfResponsibilityDemo.ActionItem; namespace ChainOfResponsibilityDemo.Handler { public abstract class Employee { /// <summary> /// 委托 /// </summary> /// <param name="e"></param> /// <param name="l"></param> public delegate void OnLeaveApplied(Employee e, Leave l); /// <summary> /// 事件 /// </summary> public event OnLeaveApplied onLeaveApplied = null; public void LeaveApplied(Employee s, Leave leave) { if (onLeaveApplied != null) { onLeaveApplied(this, leave); } } /// <summary> /// 抽象方法 /// </summary> /// <param name="leave"></param> public abstract void ApproveLeave(Leave leave); /// <summary> /// 定義屬性 員工的上級管理者 /// </summary> public Employee Supervisor { get; set; } public void ApplyLeave(Leave l) { LeaveApplied(this, l); } } }
定義員工級別:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ChainOfResponsibilityDemo.ActionItem 7 { 8 /// <summary> 9 /// 定義員工級別 10 /// </summary> 11 public class Leave 12 { 13 /// <summary> 14 /// 構造函數 15 /// </summary> 16 /// <param name="guid"></param> 17 /// <param name="days"></param> 18 public Leave(Guid guid, int days) 19 { 20 leaveID = guid; 21 numberOfDays = days; 22 } 23 24 Guid leaveID; 25 /// <summary> 26 /// 級別 27 /// </summary> 28 public Guid LeaveID 29 { 30 get { return leaveID; } 31 set { leaveID = value; } 32 } 33 int numberOfDays; 34 35 /// <summary> 36 /// 請假天數 37 /// </summary> 38 public int NumberOfDays 39 { 40 get { return numberOfDays; } 41 set { numberOfDays = value; } 42 } 43 } 44 }
定義具體處理者:
TeamLeader:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using ChainOfResponsibilityDemo.Handler; 6 using ChainOfResponsibilityDemo.ActionItem; 7 8 namespace ChainOfResponsibilityDemo.ConcreteHandlers 9 { 10 /// <summary> 11 /// TeamLeader 12 /// </summary> 13 public class TeamLeader:Employee 14 { 15 /// <summary> 16 /// 最大請假天數 17 /// </summary> 18 const int MAX_LEAVES_CAN_APPROVE = 10; 19 20 public TeamLeader() 21 { 22 this.onLeaveApplied+=new OnLeaveApplied(TeamLeader_onLeaveApplied); 23 } 24 25 protected void TeamLeader_onLeaveApplied(Employee e, Leave l) 26 { 27 //如果請假天數小於 teamleader可以批准的天數,直接批假 28 if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE) 29 { 30 ApproveLeave(l); 31 } 32 else 33 { 34 if (Supervisor != null) 35 { 36 //請上級批假 37 Supervisor.LeaveApplied(this, l); 38 } 39 } 40 } 41 42 //批假 43 public override void ApproveLeave(Leave leave) 44 { 45 Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}", 46 leave.LeaveID, leave.NumberOfDays, "Team Leader"); 47 } 48 } 49 }
ProjectLeader:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using ChainOfResponsibilityDemo.Handler; 6 using ChainOfResponsibilityDemo.ActionItem; 7 8 namespace ChainOfResponsibilityDemo.ConcreteHandlers 9 { 10 /// <summary> 11 /// 項目經理 12 /// </summary> 13 public class ProjectLeader:Employee 14 { 15 /// <summary> 16 /// 最大請假天數 17 /// </summary> 18 const int MAX_LEAVES_CAN_APPROVE = 20; 19 20 public ProjectLeader() 21 { 22 this.onLeaveApplied+=new OnLeaveApplied(ProjectLeader_onLeaveApplied); 23 } 24 25 26 protected void ProjectLeader_onLeaveApplied(Employee e, Leave l) 27 { 28 //如果請假天數小於 teamleader可以批准的天數,直接批假 29 if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE) 30 { 31 ApproveLeave(l); 32 } 33 else 34 { 35 if (Supervisor != null) 36 { 37 //請上級批假 38 Supervisor.LeaveApplied(this, l); 39 } 40 } 41 } 42 43 public override void ApproveLeave(Leave leave) 44 { 45 Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}", 46 leave.LeaveID, leave.NumberOfDays, "Project Leader"); 47 } 48 } 49 }
HR:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using ChainOfResponsibilityDemo.Handler; using ChainOfResponsibilityDemo.ActionItem; namespace ChainOfResponsibilityDemo.ConcreteHandlers { public class HR:Employee { /// <summary> /// 最大請假天數 /// </summary> const int MAX_LEAVES_CAN_APPROVE = 30; public HR() { this.onLeaveApplied += new OnLeaveApplied(HR_onLeaveApplied); } void HR_onLeaveApplied(Employee e, Leave l) { if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE) { ApproveLeave(l); } else { if (Supervisor != null) { Supervisor.LeaveApplied(this, l); } else { Console.WriteLine("您的假期超過最高限制,請聯系HR!"); } } } public override void ApproveLeave(Leave leave) { Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}", leave.LeaveID, leave.NumberOfDays, "HR"); } } }
客戶端調用:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using ChainOfResponsibilityDemo.ConcreteHandlers; using ChainOfResponsibilityDemo.ActionItem; namespace ChainOfResponsibilityDemo { class Program { static void Main(string[] args) { //創建員工對象 TeamLeader tl = new TeamLeader(); ProjectLeader pl = new ProjectLeader(); HR hr = new HR(); //設置員工上級 tl.Supervisor = pl; pl.Supervisor = hr; //請假5天 tl.ApplyLeave(new Leave(Guid.NewGuid(), 5)); //請假15天 tl.ApplyLeave(new Leave(Guid.NewGuid(), 15)); //請假25天 tl.ApplyLeave(new Leave(Guid.NewGuid(), 25)); //請假35天 tl.ApplyLeave(new Leave(Guid.NewGuid(), 35)); Console.ReadLine(); } } }
輸出結果:
模式分析
在職責鏈模式里,很多對象
由每一個對象對其下家的引用而連接起來形成一條鏈。
請求在這條鏈上傳遞,直到鏈上的某一個對象處理此請求為止。
發出這個請求的客戶端並不知道鏈上的哪一個對象最終處理這個請求,這使得
系統可以在不影響客戶端的情況下動態地重新組織鏈和分配責任。
模式優缺點
職責鏈模式的優點
• 降低耦合度
• 可簡化對象的相互連接
• 增強給對象指派職責的靈活性
• 增加新的請求處理類很方便
職責鏈模式的缺點
•不能保證請求一定被接收。
•系統性能將受到一定影響,而且在進行代碼調試時不太方便;可能會造成循環調用。
模式適用環境
在以下情況下可以使用職責鏈模式:
•有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
•在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
•可動態指定一組對象處理請求。
總結
行為型模式是對在不同的對象之間划分責任和算法的抽象化。行為型模式不僅僅關注類和對象的結構,而且重點關注它們之間的相互作用。通過行為型模式,可以更加清晰地划
分類與對象的職責,並研究系統在運行時實例對象之間的交互。行為型模式可以分為類行為型模式和對象行為型模式兩種。
職責鏈模式可以避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿着這條鏈傳遞請求,直到有對象處理它為止。它是一種對象行為型模式。
職責鏈模式包含兩個角色:抽象處理者定義了一個處理請求的接口;具體處理者是抽象處理者的子類,它可以處理用戶請求。
在職責鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。發出這個請求的客戶端並不知
道鏈上的哪一個對象最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織鏈和分配責任。
職責鏈模式的主要優點在於可以降低系統的耦合度,簡化對象的相互連接,同時增強給對象指派職責的靈活性,增加新的請求處理類也很方便;其主要缺點在於不能保證請求一
定被接收,且對於比較長的職責鏈,請求的處理可能涉及到多個處理對象,系統性能將受到一定影響,而且在進行代碼調試時不太方便。
一個請求;可動態指定一組對象處理請求。