小看--职责链模式

Posted 山的那边是什么

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小看--职责链模式相关的知识,希望对你有一定的参考价值。

      职责链模式,是属于行为型设计模式,可以把一个请求分给多个对象处理的机会,可以避免发送者和处理者之间的耦合。

   (二)职责链模式的演变

      现在员工需要请假,请假审批规则如下:

      一天之内:组长审批通过即可,无需报送到部门经理;

      一天到三天:部门经理审批即可,无效报送到中心老大;

      三天到三十天:部门老大审批,无需报送到总裁办;

      大于三十天:总裁办审批;

      对于上面这个需求,开发者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、要注意链的有效性。

       

      

     

          

以上是关于小看--职责链模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式 职责链模式

职责链模式vs状态模式区别

利用职责链模式分解If else

设计模式 行为型模式 -- 职责链模式(JDK源码分析:FilterChain(过滤器))

JAVA设计模式大总结(二十三)--- b站尚硅谷设计模式之 职责链模式 图解 + 代码 整理(超详)

职责链模式