小看--職責鏈模式
職責鏈模式,是屬於行為型設計模式,可以把一個請求分給多個對象處理的機會,可以避免發送者和處理者之間的耦合。
(二)職責鏈模式的演變
現在員工需要請假,請假審批規則如下:
一天之內:組長審批通過即可,無需報送到部門經理;
一天到三天:部門經理審批即可,無效報送到中心老大;
三天到三十天:部門老大審批,無需報送到總裁辦;
大於三十天:總裁辦審批;
對於上面這個需求,開發者A拿到之後,馬上寫出了如下代碼;
1 、定義一個請假的上下文
/// <summary> /// 請假的上下文/// </summary> public class ApplyContext { public int Id { get; set; } /// <summary> /// 發起人 /// </summary> public string Name { get; set; } /// <summary> /// 請假天數 /// </summary> public int Day { get; set; }/// <summary> /// 請假描述 /// </summary> public string Description { get; set; } /// <summary> /// 審批結果 /// </summary> public bool AuditResult { get; set; } /// <summary> /// 審批備註 /// </summary> public stringAudtiRemark { get; set; } }
2、根據需求,做了如下實現
var applyContext=new ApplyContext() { Id =1, Name ="小張", Day =4, Description ="老家有事" }; if (applyContext.Day <= 1) { Console.WriteLine("組長{0}審批....","groupleader"); applyContext.AuditResult = true; } if (applyContext.Day > 1 && applyContext.Day <= 3) { Console.WriteLine("部門經理{0}審批....", "groupleader"); applyContext.AuditResult = true; } //.....下面也是類似的,if-else這種做法。/ }
對於開發者A的做法,發現了這個基本上就是面向過程的編程,沒有面向對象的思想(違法了單一職責原則),把所有東西都暴露到了上端。針對上面不好的地方,我們來改進我們的代碼。
下面是開發者B的做法。
開發者B呢,就有一點面向對象的思想了,我們先把每個審批者提取出來,分別是組長,部門經理,中心老大,總裁。他們都有一個共同的操作,就是批假,再提取一個抽象出來(抽象類/接口),代碼改進如下:
1、先抽取一個抽象出來,這裏我們用抽象類
public abstract class AbstractAudtitor { /// <summary> /// 審批人的名字 /// </summary> public string Name { get; set; } /// <summary> /// 審批的方法 /// </summary> /// <param name="context"></param> public abstract void Audit(ApplyContext context); }
2、每個類,分別繼承這個接口
public class CenterLeader:AbstractAudtitor { public override void Audit(ApplyContext context) { if (context.Day >3&&context.Day<=30) { Console.WriteLine($"{this.Name},中心領導審批"); context.AuditResult = true; } } }
上面代碼,每個類都是類似的,在這裏不一一列舉;
3、調用過程
AbstractAudtitor groupLeader = new GroupLeader() { Name = "zhang", }; groupLeader.Audit(applyContext); if (!applyContext.AuditResult) { AbstractAudtitor deptManager = new DepartmentManager() { Name = "部門領導", }; deptManager.Audit(applyContext); if (!applyContext.AuditResult) { AbstractAudtitor centerLeader = new CenterLeader() { Name = "中心老大" }; centerLeader.Audit(applyContext); } else if (!applyContext.AuditResult) { AbstractAudtitor ceo = new CEO() { Name = "總裁辦" }; ceo.Audit(applyContext); } else { Console.WriteLine("領導不批準!"); } }
從上面可以看出,雖然我們現在是面向對象了,但是我們只是簡單的翻譯了一下需求,完全沒有站在實際情況中去考慮, 因為實際情況中,不可能是你一個普通員工,說給請假條給部門領導,中心老大,總裁辦的,而是由各個層級之間不斷的轉發,換句話說,上端不應該去找各個環節,應該是各個層級之間互相轉發。
那麽接下來,開發者C根據剛剛所說,寫了如下代碼:
主要思路是:既然你說上端不要直接去找各個環節,那麽我們就把這個找的動作,放到每一個環節類的後面,也就是做如下改變
public class CenterLeader : AbstractAudtitor { public override void Audit(ApplyContext context) { if (context.Day > 3 && context.Day <= 30) { Console.WriteLine($"{this.Name},中心領導審批"); context.AuditResult = true; } else { //在中心領導類裏面,主動去找下一層,也就是總裁辦的人。 new CEO() {Name = "zz"}.Audit(context); } } }
其他類,也是做了如下改變。確實我們按照上面這樣做法,很滿足我們的現在的需求,可能有一天領導說:這個請假流程得變更一下,或者請假審批的下一級,應該是可以動態指定的,流程是可以動態配置的。
為了滿足這個需要,我們又要對上面的代碼進行改進;
根據領導說:流程是要可以動態指定的,行,按照設計模式的原則,封裝不變的,變化的就拋出去,我們大概做了如下改進:我們提供一個SetNext(),設置下一個環節的方法。代碼如下:
因為每個環節都要提供一個SetNext方法,並且都需要一個AuditNext()的一個判斷,所以我們就直接提供到父類AbstractAudtitor裏面,具體代碼如下:
1 先完善了剛才的父類。
public abstract class AbstractAudtitor { public string Name { get; set; } protected AbstractAudtitor _NextAuditor = null; /// <summary> /// 因為這個是父類,在這邊寫一個設置下一個環節的方法。 /// </summary> /// <param name="audtitor"></param> public void SetNext(AbstractAudtitor audtitor) { this._NextAuditor = audtitor; } public abstract void Audit(ApplyContext context); //下一個環節審批 protected void AuditNext(ApplyContext context) { if (this._NextAuditor != null) { this._NextAuditor.Audit(context); } else { context.AuditResult = false; context.AudtiRemark = "不允許請假!"; } } }
2 調用方式如下:
AbstractAudtitor groupLeader=new GroupLeader() { Name ="zz", }; AbstractAudtitor deptManager = new DepartmentManager() { Name = "經理", }; AbstractAudtitor centerLeader = new CenterLeader() { Name = "中心領導", }; AbstractAudtitor ceo = new CEO() { Name = "CEO", }; //設置下一個環節 groupLeader.SetNext(deptManager); deptManager.SetNext(centerLeader); centerLeader.SetNext(centerLeader); //上面就是設置流程 //發起流程 groupLeader.Audit(applyContext);
上面上端就可以對流程進行動態配置了,當然這裏我們還需要用反射加配置文件的形式,把我們的流程做到更加可配置化。
(三)職責鏈模式的優缺點
優點:1 、請求者和處理者松耦合;2、職責明確,可動態配置。
缺點:1、產生了很多細粒對象;2、要註意鏈的有效性。
小看--職責鏈模式