责任链模式

24 阅读5分钟

为请求创建了一个接收者对象的链。在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

比如请假场景,上级审批不通过,交给上上级审批 责任链模式,客户将请求发送给责任链即可,不需要关注请求的处理过程,将请求的发送者和请求的处理者解耦了。比如请假场景,0-1天主管审批、1-3天科长审批、3-7天经理审批。需要有主管、科长、经理三个类,每个类中有一个审批方法,没有任何设计的话,可能在审批方法中写很多if-else-if这种判断,来交给下一个审批人。但是审批人可能是变化的,比如取消主管职位,我们的类会需要频繁修改,变得不稳定。这时我们可以在类中加一个下一个审批人的属性,和设置下一个审批人的方法,在审批方法中交给下一个审批人,至于下一个审批人是谁,交给上端指定。

ApplyContext代表请假条。

**Context是责任链模式的标配,行为转移到哪儿,context会跟到哪儿

示例:请假流程

public class ApplyContext
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        /// <summary>
        /// 请假时长
        /// </summary>
        public int Hour { get; set; }
        /// <summary>
        /// 请教的描述信息
        /// </summary>
        public string? Description { get; set; }
        /// <summary>
        /// 请假是否审批通过
        /// </summary>
        public bool AuditResult { get; set; }
        /// <summary>
        /// 审批备注
        /// </summary>
        public string? AuditRemark { get; set; }
    }

请假写法

面向过程写法

//请假条
ApplyContext context = new ApplyContext() //标配---
{
    Id = 506,
    Name = "Join",
    Hour = 30,
    Description = "朝夕教育线下沙龙",
    AuditResult = false,
    AuditRemark = ""
};
{
    if (context.Hour <= 8) {
        context.AuditResult = true;
        context.AuditRemark = "PM审批通过";
    }
    else {
        if (context.Hour < 16) {
            context.AuditResult = true;
            context.AuditRemark = "主管审批通过";
        }
        else {
            //....  后续还有很多判断
        }
    }
}

问题:

完全是面向过程式编程,所有的业务逻辑完全暴露在上端,完全没有任何面向对象的思想(继承--封装--多态)

改进一:面向对象写法

改进一:POP--OOP,改为面向对象编程

  1. 对象:请假条,PM,Charge,Manager经理
  2. 确定对象--封装业务逻辑-- - 封装
  3. 继承去掉重复代码-- - 继承
  4. 统一类下的同一个方法执行的不同的业务逻辑----多态

public class PM 
    {
        public int Id { get; set; }
        public string? Name { get; set; }

        //审批动作
        public  void Audit(ApplyContext context)
        {
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "PM审批通过";
            }
            else
            { 
                //...
            }
        }
    }
public class Charge 
    {
        public int Id { get; set; }
        public string? Name { get; set; }

        public  void Audit(ApplyContext context)
        {
            if (context.Hour <= 16)
            {
                context.AuditResult = true;
                context.AuditRemark = "主管审批通过";
            }
            else
            {
            	//...
            }
        }
    }

2、抽象出父类,子类继承父类

public abstract class AbstractAuditor
    {
        public int Id { get; set; }
        public string? Name { get; set; } 
        public abstract void Audit(ApplyContext context);
    }

子类

public class PM : AbstractAuditor
    {
        //审批动作
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "PM审批通过";
            }
            else
            { 
                //...
            }
        }
    }

public class Charge : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 16)
            {
                context.AuditResult = true;
                context.AuditRemark = "主管审批通过";
            }
            else
            {
            	//...
            }
        }
    }

上端调用

//子类继承父类,可以使用父类去声明一个子类
{
    AbstractAuditor pm = new PM() {
        Id = 123,
        Name = "小黄鱼"
    };
    pm.Audit(context);
    if (!context.AuditResult) {
        AbstractAuditor charge = new Charge() //自己去找主管
        {
            Id = 234,
            Name = "1同学"
        };
        charge.Audit(context);
    }
}

代码其实不符合业务逻辑。

正确的业务逻辑应该是 请假者--提交请假条给PM--PM能审批通过就通过了,如果不能审批通过。应该由PM自动转交给主管。

改进二:向实体类添加业务逻辑

public class PM : AbstractAuditor
    {
        //审批动作
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "PM审批通过";
            }
            else
            { 
                //自动转交给主管...
                AbstractAuditor charge = new Charge()
                {
                    Id = 234,
                    Name = "1同学"
                };
                charge.Audit(context);
            }
        }
    }

public class Charge : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 16)
            {
                context.AuditResult = true;
                context.AuditRemark = "主管审批通过";
            }
            else
            {
            	//自动转交给经理...
                AbstractAuditor manager = new Manager() {
                    Id = 456,
                    Name = "Join"
                };
                manager.Audit(context);
            }
        }
    }

public class Manager : AbstractAuditor
    {

        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 32)
            {
                context.AuditResult = true;
                context.AuditRemark = "经理审批通过";
            }
            else
            {
                //...
            }
        }
    }

上端调用

{
    AbstractAuditor pm = new PM() {
        Id = 123,
        Name = "小黄鱼"
    };
    pm.Audit(context);
}

分析:

开发的功能只是符合了当前的业务需求。没有考虑到将来可能会发生什么变化。

比如组织架构发生改变:主管的职位,不要主管的这个职位了。必须要修改之前的代码,这就违背了开闭原则。代码的不稳定,就要想办法让代码稳定。

改进三:将流程中可配置部分甩锅出去

public class PM : AbstractAuditor
    {
        private AbstractAuditor _NextAuditor;
    	//设置下一个审批者
        public void SetNextAuditor(AbstractAuditor nextAuditor) {
            this._NextAuditor = nextAuditor;
        }
        //审批动作
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "PM审批通过";
            }
            else
            { 
                //这里可能会代码不稳定,这个锅,我不背---甩锅给别人。让别人来背过。 
                //不能写死~--甩锅出去 
                _NextAuditor?.Audit(context);
            }
        }
    }

public class Charge : AbstractAuditor
    {
        private AbstractAuditor _NextAuditor;
    	//设置下一个审批者
        public void SetNextAuditor(AbstractAuditor nextAuditor) {
            this._NextAuditor = nextAuditor;
        }
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 16)
            {
                context.AuditResult = true;
                context.AuditRemark = "主管审批通过";
            }
            else
            {
            	//甩锅出去
                _NextAuditor?.Audit(context);
            }
        }
    }

public class Manager : AbstractAuditor
    {
        private AbstractAuditor _NextAuditor;
    	//设置下一个审批者
        public void SetNextAuditor(AbstractAuditor nextAuditor) {
            this._NextAuditor = nextAuditor;
        }
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 32)
            {
                context.AuditResult = true;
                context.AuditRemark = "经理审批通过";
            }
            else
            {
                //甩锅出去
                _NextAuditor?.Audit(context);
            }
        }
    }

抽取到公共父类

public abstract class AbstractAuditor
    {
        public int Id { get; set; }
        public string? Name { get; set; } 
        public abstract void Audit(ApplyContext context);

        protected AbstractAuditor _NextAuditor;
        /// <summary>
        /// 设置下一个审批者
        /// </summary>
        public void SetNextAuditor(AbstractAuditor nextAuditor)
        {
            this._NextAuditor = nextAuditor;
        }
    }

实现父类的子类

public class PM : AbstractAuditor
    {
        //审批动作
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "PM审批通过";
            }
            else
            { 
                //这里可能会代码不稳定,这个锅,我不背---甩锅给别人。让别人来背过。 
                //不能写死~--甩锅出去 
                _NextAuditor?.Audit(context);
            }
        }
    }

public class Charge : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 16)
            {
                context.AuditResult = true;
                context.AuditRemark = "主管审批通过";
            }
            else
            {
            	//甩锅出去
                _NextAuditor?.Audit(context);
            }
        }
    }

public class Manager : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            if (context.Hour <= 32)
            {
                context.AuditResult = true;
                context.AuditRemark = "经理审批通过";
            }
            else
            {
                //甩锅出去
                _NextAuditor?.Audit(context);
            }
        }
    }

上端调用

AbstractAuditor pm = new PM()
{
    Id = 123,
    Name = "小黄鱼"
};
AbstractAuditor charge = new Charge() {
    Id = 234,
    Name = "1同学"
};
AbstractAuditor manager = new Manager()
{
    Id = 345,
    Name = "join"
}; 
pm.SetNextAuditor(manager);
charge.SetNextAuditor(manager);
pm.Audit(context);

甩锅后:

1.需要上端来配置审批流程。可以灵活配置审批环节

2.如果组织架构发生变化了,如取消主管的职位。修改装配环节即可

3.上端的代码变复杂了,但有办法解决的。-要另外的设计模式来协助解决,建造者模式

改进四:建造者模式解决上端代码复杂

AbstractAuditor pm = new PM()
{
    Id = 123,
    Name = "小黄鱼"
};
AbstractAuditor charge = new Charge() {
    Id = 234,
    Name = "1同学"
};
AbstractAuditor manager = new Manager()
{
    Id = 345,
    Name = "join"
}; 
pm.SetNextAuditor(manager);
charge.SetNextAuditor(manager);

return pm

上端调用