1. 程式人生 > >小看--職責鏈模式

小看--職責鏈模式

ont 沒有 ply mar ons 似的 發現 父類 string

職責鏈模式,是屬於行為型設計模式,可以把一個請求分給多個對象處理的機會,可以避免發送者和處理者之間的耦合。

(二)職責鏈模式的演變

現在員工需要請假,請假審批規則如下:

一天之內:組長審批通過即可,無需報送到部門經理;

一天到三天:部門經理審批即可,無效報送到中心老大;

三天到三十天:部門老大審批,無需報送到總裁辦;

大於三十天:總裁辦審批;

對於上面這個需求,開發者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 string
AudtiRemark { 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、要註意鏈的有效性。

小看--職責鏈模式