講故事 書接上文, "狀態模式" 完美解決了多判斷分支分支問題,符合了我 "人生信條" 的第一、第三條。今天來探討一下狀態模式異父異母的親兄弟 職責鏈模式 ,它們都有異曲同工之妙,實際開發中可根據口味,自行選用。 今天的故事背景就放在我們平時 申請加薪、請假等活動中,我們都知道,隨著我們申請內容的不 ...
講故事
書接上文,狀態模式完美解決了多判斷分支分支問題,符合了我人生信條的第一、第三條。今天來探討一下狀態模式異父異母的親兄弟職責鏈模式,它們都有異曲同工之妙,實際開發中可根據口味,自行選用。
今天的故事背景就放在我們平時 申請加薪、請假等活動中,我們都知道,隨著我們申請內容的不同,審批人員的等級也不同。我們就先用最簡單的代碼模擬一下
Coding
RequestType枚舉,規範我們申請的類型
/// <summary> /// 請求類型 /// </summary> public enum RequestType { /// <summary> /// 請假 /// </summary> Leave, /// <summary> /// 加薪 /// </summary> PayRise }
Requset類,簡單描述申請內容
/// <summary> /// 請求 /// </summary> public class Requset { /// <summary> /// 請求類型 /// </summary> public RequestType Type { get; set; } /// <summary> /// 請求數量 /// </summary> public int Number { get; set; } /// <summary> /// 請求說明 /// </summary> public string Content { get { switch (Type) { case RequestType.Leave: return $"請假{Number}天"; case RequestType.PayRise: return $"加薪{Number}元"; default: return "未知"; } } } }
ManagerType枚舉,定義不同的審批人員類型
/// <summary> /// 管理者類型 /// </summary> public enum ManagerType { PM, CTO, CEO }
Manager類,審批人員,處理我們的請求
public class Manager { private readonly ManagerType _managerType; public Manager(ManagerType managerType) { _managerType = managerType; } /// <summary> /// 處理請求 /// </summary> public void Process(Requset requset) { if (_managerType == ManagerType.PM) { if (requset.Type == RequestType.Leave && requset.Number <= 2) { Console.WriteLine($"項目經理 已批准 你的 {requset.Content} 申請"); } else { Console.WriteLine($"項目經理 無權批准 你的 {requset.Content} 申請"); } } else if (_managerType == ManagerType.CTO) { if (requset.Type == RequestType.Leave) { if (requset.Number <= 5) { Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申請"); } else { Console.WriteLine($"CTO 無權批准 你的 {requset.Content} 申請"); } } else { if (requset.Number <= 500) { Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申請"); } else { Console.WriteLine($"CTO 無權批准 你的 {requset.Content} 申請"); } } } else if (_managerType == ManagerType.CEO) { if (requset.Type == RequestType.Leave) { Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申請"); } else { if (requset.Number <= 1000) { Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申請"); } else { Console.WriteLine($"CEO對你的 {requset.Content} 申請 說:“小子,你有點飄啊!”"); } } } } }
客戶端
internal class Program
{
private static void Main(string[] args)
{
//創建領導
var pm = new Manager(ManagerType.PM);
var cto = new Manager(ManagerType.CTO);
var ceo = new Manager(ManagerType.CEO);
//創建 請假請求
var request1 = new Requset
{
Type = RequestType.Leave,
Number = 5
};
pm.Process(request1);
cto.Process(request1);
ceo.Process(request1);
Console.WriteLine("\n");
//創建 加薪請求
var request2 = new Requset
{
Type = RequestType.PayRise,
Number = 2000
};
pm.Process(request2);
cto.Process(request2);
ceo.Process(request2);
Console.WriteLine("\nHappy Ending~");
Console.ReadLine();
}
}
結果展示:
項目經理 無權批准 你的 請假5天 申請
CTO 已批准 你的 請假5天 申請
CEO 已批准 你的 請假5天 申請
項目經理 無權批准 你的 加薪2000元 申請
CTO 無權批准 你的 加薪2000元 申請
CEO對你的 加薪2000元 申請 說:“小子,你有點飄啊!”
我們Code Review後,就會發現Manager.Process()又醜又長呀,同樣是我們選侍女時出現的三個問題:方法長,分支多,難維護。
那我們怎麼使用職責鏈模式來解決呢?我們先來瞭解一下它...
職責鏈模式
敲黑板·劃重點
定義: 使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這個對象連成一個鏈,並沿著這條鏈傳遞請求,知道有一個對象處理請求為止。
好處: 接收者和發送者都沒有對方的明確信息,且鏈中的對象自己也並不知道鏈的結構。結果是職責鏈可簡化對象的相互連接,它們僅需保持一個指向其後繼者的引用,而不需要保持它所有的候選接受者的引用。【降低耦合度】
註意: 一個請求極有可能到了鏈的末端都得不到處理,或者因為沒有正確配置而得不到處理,需要考慮全面!
在下麵代碼優化中來深化對定義、好處的理解吧
Code Upgrade
Manager抽象類,不同許可權審批人員的基類,可以設置下一級審批人員,形成一條職責鏈
public abstract class Manager { protected readonly ManagerType _managerType; /// <summary> /// 下一個處理者 /// </summary> protected Manager Successor { get; private set; } public Manager(ManagerType managerType) { _managerType = managerType; } /// <summary> /// 設置下一個處理者 /// </summary> /// <param name="manager"></param> public void SetSuccessor(Manager manager) { Successor = manager; } /// <summary> /// 處理請求 /// </summary> /// <param name="requset"></param> public abstract void Process(Requset requset); }
PM、CTO、CEO類,具體的審批人,實現處理方法Process()
public class PM : Manager { public PM(ManagerType managerType) : base(managerType) { } public override void Process(Requset requset) { if (requset.Type == RequestType.Leave && requset.Number <= 2) { Console.WriteLine($"項目經理 已批准 你的 {requset.Content} 申請"); return; } if (Successor != null) { //交由下一級處理 Successor.Process(requset); } } } public class CTO : Manager { public CTO(ManagerType managerType) : base(managerType) { } public override void Process(Requset requset) { if (requset.Type == RequestType.Leave && requset.Number <= 5) { Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申請"); return; } if (requset.Type == RequestType.PayRise && requset.Number <= 500) { Console.WriteLine($"CTO 已批准 你的 {requset.Content} 申請"); return; } if (Successor != null) { //交由下一級處理 Successor.Process(requset); } } } public class CEO : Manager { public CEO(ManagerType managerType) : base(managerType) { } public override void Process(Requset requset) { if (requset.Type == RequestType.Leave && requset.Number <= 15) { Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申請"); return; } if (requset.Type == RequestType.PayRise && requset.Number <= 1000) { Console.WriteLine($"CEO 已批准 你的 {requset.Content} 申請"); return; } Console.WriteLine($"CEO對你的 {requset.Content} 申請 說:“小子,你有點飄啊!”"); } }
客戶端代碼
internal class Program
{
private static void Main(string[] args)
{
//創建領導
var pm = new PM(ManagerType.PM);
var cto = new CTO(ManagerType.CTO);
var ceo = new CEO(ManagerType.CEO);
//設置下一級處理者
pm.SetSuccessor(cto);
cto.SetSuccessor(ceo);
//創建 請假請求
var request1 = new Requset
{
Type = RequestType.Leave,
Number = 5
};
pm.Process(request1);
Console.WriteLine("\n");
//創建 加薪請求
var request2 = new Requset
{
Type = RequestType.PayRise,
Number = 2000
};
pm.Process(request2);
Console.WriteLine("\nHappy Ending~");
Console.ReadLine();
}
}
結果展示:
CTO 已批准 你的 請假5天 申請
CEO對你的 加薪2000元 申請 說:“小子,你有點飄啊!”
這樣我們也消除上面三個問題。當需求需要人事加入審批流程時,只需要增加一個繼承Manager的人事類;當需求要求不需要CTO審批,直接由PM轉到CEO時,我們只用修改PM的SetSuccessor(),這也體現了職責鏈的靈活性。
示例代碼地址: https://gitee.com/sayook/DesignMode/tree/master/ChainOfResponsibility