關於 Chain of Responsibility 本篇將討論以下幾個問題
1. 關於 Chain of Responsibility
2. UML
3. 將 UML 轉為程式碼
4. 情境
測試環境:
OS:Windows 10
IDE:Visual Studio 2019
1. 關於 Chain of Responsibility
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
by Gang of Four
- 藉由給一個以上對像處理請求的機會,避免將請求的發送者與接收者耦合
- 鏈接接收對象,並將請求沿著鏈傳遞,直到對象處理該請求
Chain of Responsibility(責任鏈)屬於行為型(Behavioral Patterns),當遇到未知請求 & 未知處理者時,可使用 Chain of Responsibility 來將請求依序對應全部處理者,找尋可處理請求的對象。當遇到過濾、攔截的需求時,可使用 Chain of Responsibility 來依照指定順序執行判斷邏輯。
優點:
- 符合 單一職責原則(Single Responsibility Principle)
- 符合 開閉原則(Open Closed Principle)
缺點:
- 用於未知請求 & 未知處理者時,可能會因為處理者過多而影響效能
2. UML
Class 間關聯:
- Client 關聯 Handler
- ConcreteHandler 1 / 2 繼承 Handler
- ConcreteHandler2 可包含 Handler
Class:
- Client:呼叫端
- Handler:處理請求的介面
- ConcreteHandler:處理請求實作
3. 將 UML 轉為程式碼
處理請求的抽象類別
/// <summary>
/// 處理請求的抽象類別
/// </summary>
public abstract class Handler
{
protected Handler successor;
public void SetSuccessor(Handler successor)
{
this.successor = successor;
}
public abstract void HandleRequest(int request);
}
處理請求實作 1 / 2 / 3
/// <summary>
/// 處理請求實作
/// </summary>
public class ConcreteHandler1 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 0 && request < 10)
{
Console.WriteLine($"{GetType().Name} handled request {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
/// <summary>
/// 處理請求實作
/// </summary>
public class ConcreteHandler2 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 10 && request < 20)
{
Console.WriteLine($"{GetType().Name} handled request {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
/// <summary>
/// 處理請求實作
/// </summary>
public class ConcreteHandler3 : Handler
{
public override void HandleRequest(int request)
{
if (request >= 20 && request < 30)
{
Console.WriteLine($"{GetType().Name} handled request {request}");
}
else if (successor != null)
{
successor.HandleRequest(request);
}
}
}
- 建立
handler
- 經由
SetSuccessor
設置接續執行的handler
- 依序傳入測試資料
static void Main(string[] args)
{
Default.Handler handler1 = new Default.ConcreteHandler1();
Default.Handler handler2 = new Default.ConcreteHandler2();
Default.Handler handler3 = new Default.ConcreteHandler3();
handler1.SetSuccessor(handler2);
handler2.SetSuccessor(handler3);
var requests = new[]{ 5, 12, 24 };
Console.WriteLine("Array elements: 5, 12, 24");
foreach (var request in requests)
{
handler1.HandleRequest(request);
}
Console.ReadLine();
}
執行結果
Array elements: 5, 12, 24
ConcreteHandler1 handled request 5
ConcreteHandler2 handled request 12
ConcreteHandler3 handled request 24
4. 情境
我們接到了一個員工請假簽核的需求
- 三天內(包含三天)店長同意即可
- 三到七天(包含七天)店長 & 區主管同意
- 超過七天則要店長 & 區主管 & 總經理同意
- 假單需依序簽核
請假簽核抽象類別
/// <summary>
/// 請假簽核抽象類別
/// </summary>
public abstract class Leave
{
protected Leave successor;
public void SetSuccessor(Leave successor)
{
this.successor = successor;
}
public abstract void TakeALeave(int days);
}
店長 & 區主管 & 總經理簽核實作
/// <summary>
/// 店長簽核實作
/// </summary>
public class StoreManagerApprove : Leave
{
public override void TakeALeave(int days)
{
if (days <= 3)
{
Console.WriteLine($"Store Manager : OK~");
}
else if (successor != null)
{
Console.WriteLine($"Store Manager : OK~");
successor.TakeALeave(days);
}
}
}
/// <summary>
/// 區主管簽核實作
/// </summary>
public class DistrictManagerApprove : Leave
{
public override void TakeALeave(int days)
{
if (days <= 7)
{
Console.WriteLine($"District Manager : OK~");
}
else if (successor != null)
{
Console.WriteLine($"District Manager : OK~");
successor.TakeALeave(days);
}
}
}
/// <summary>
/// 總經理簽核實作
/// </summary>
public class GeneralManagerApprove : Leave
{
public override void TakeALeave(int days)
{
if (days > 7)
{
Console.WriteLine($"General Manager : OK~");
}
}
}
- 建立請假流程
handler
- 經由
SetSuccessor
設置接續執行的主管 - 依序傳入請假天數(1、5、10 天)
static void Main(string[] args)
{
Situation.StoreManagerApprove storeManager = new Situation.StoreManagerApprove();
Situation.DistrictManagerApprove districtManager = new Situation.DistrictManagerApprove();
Situation.GeneralManagerApprove generalManager = new Situation.GeneralManagerApprove();
storeManager.SetSuccessor(districtManager);
districtManager.SetSuccessor(generalManager);
Console.WriteLine($"請假 1 天");
storeManager.TakeALeave(1);
Console.WriteLine($"\n");
Console.WriteLine($"請假 5 天");
storeManager.TakeALeave(5);
Console.WriteLine($"\n");
Console.WriteLine($"請假 10 天");
storeManager.TakeALeave(10);
Console.WriteLine($"\n");
Console.ReadLine();
}
執行結果
請假 1 天
Store Manager : OK~
請假 5 天
Store Manager : OK~
District Manager : OK~
請假 10 天
Store Manager : OK~
District Manager : OK~
General Manager : OK~
完整程式碼
GitHub:Behavioral_01_ChainOfResponsibility