C# 设计模式之职责链模式

总目录


前言

职责链,就是一条处理请求的职责链条,生活中常见的就是 请假申请,例如你家里有事情,提交了请假申请,然后公司规定:

当 请假天数 <= 3天,由项目主管审批,

当 3天 < 请假天数 <= 10天,由经理审批,

当 请假天数 > 10天,由CEO审批。

这就是一条职责链。


1 基础介绍

  1. 定义:某个请求需要多个对象进行处理,从而避免请求的发送者和接收之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
  2. "责任链"顾名思义,是指一个需要负责处理请求的链条。每个链条节点都是一个单独的责任者,由责任者自己决定是否处理请求或交给下一个节点。
  3. 职责链模式中的角色:
    • 抽象处理者角色(Handler):抽象处理者定义了一个处理请求的接口,它一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个自类型的对象,作为其对下家的引用。通过该引用,处理者可以连成一条链。
    • 具体处理者角色(ConcreteHandler):具体处理者是抽象处理者的子类,它可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发。

2 使用场景

  • 一个系统的审批需要多个对象才能完成处理的情况下,例如请假系统等。
  • 代码中存在多个if-else语句的情况下,此时可以考虑使用责任链模式来对代码进行重构
  • 有多个对象可以处理同一个请求,具体哪个对象处理该请求有运行时刻自动确定。客户端只需将请求提交到链上,无须关心请求的处理对象是谁以及它是如何处理的。
  • 不明确指定接受者的情况下,向多个对象中的一个提交一个请求。请求的发送者与请求者解耦,请求将沿着链进行传递,寻求响应的处理者。
  • 可动态指定一组对象处理请求。客户端可以动态创建职责链来处理请求,还可以动态改变链中处理者之间的先后次序

3 实现方式

我们以公司内请假为例:

当 请假天数 <= 3天,由项目主管审批,

当 3天 < 请假天数 <= 10天,由经理审批,

当 请假天数 > 10天,由CEO审批。

1. 传统实现方式

csharp 复制代码
    public class LeaveRequest
    {
        //请假人姓名
        public string Name { get; set; }
        //请假天数
        public int Num { get; set; }
        //请假事由
        public string Reason { get; set; }

        public LeaveRequest(string name,int num,string reason)
        {
            Name= name;
            Num= num;
            Reason= reason;
        }
    }


    public abstract class Handler
    {

        public abstract void HandleRequest();
    }

    public class ProjectLeader : Handler
    {
        public override void HandleRequest()
        {
            Console.WriteLine("ProjectLeader:审批通过");
        }
    }

    public class Manager : Handler
    {
        public override void HandleRequest()
        {
            Console.WriteLine("Manager:审批通过");
        }
    }

    public class CEO : Handler
    {
        public override void HandleRequest()
        {
            Console.WriteLine("CEO:审批通过");        }
    }

客户端调用:

csharp 复制代码
        static void Main(string[] args)
        {
            LeaveRequest leaveRequest = new LeaveRequest("张三",12,"事假");
            ProjectLeader projectLeader = new ProjectLeader();
            Manager manager = new Manager();
            CEO  cEO = new CEO();
            if (leaveRequest.Num <= 3)
            {
                projectLeader.HandleRequest();
            }
            else if (leaveRequest.Num > 3 && leaveRequest.Num <= 10)
            {
                manager.HandleRequest();
            }
            else
            {
                cEO.HandleRequest();
            }

            Console.ReadKey();
        }

如果我们请假的规则发生变化,例如我们新增一个10-15天的请假审批,我们需要再新增一个else if 的判断,这样是违背开闭原则的,因此我们改进一下代码。

2. 职责链模式实现方式

csharp 复制代码
	//请假申请
    public class LeaveRequest
    {
        //请假人姓名
        public string Name { get; set; }
        //请假天数
        public int Num { get; set; }
        //请假事由
        public string Reason { get; set; }

        public LeaveRequest(string name,int num,string reason)
        {
            Name= name;
            Num= num;
            Reason= reason;
        }
    }

    public abstract class Handler
    {
        // 下一个处理人,也就是下一位审批人
        public Handler NextHandler { get; set; }
        //审批人姓名
        public string Name { get; set; }
        public Handler(string name)
        {
            this.Name = name;
        }

        public abstract void HandleRequest(LeaveRequest leaveRequest);
    }

    public class ProjectLeader : Handler
    {
        public ProjectLeader(string name) : base(name)
        {
        }

        public override void HandleRequest(LeaveRequest leaveRequest)
        {
            if (leaveRequest.Num<=3)
            {
                Console.WriteLine($"项目主管:{this.Name} 审批了{leaveRequest.Name} {leaveRequest.Num}天的请假申请");
                return;
            }
            this.NextHandler?.HandleRequest(leaveRequest);
        }
    }

    public class Manager : Handler
    {
        public Manager(string name) : base(name)
        {
        }

        public override void HandleRequest(LeaveRequest leaveRequest)
        {
            if (leaveRequest.Num>3&&leaveRequest.Num <= 10)
            {
                Console.WriteLine($"经理:{this.Name} 审批了{leaveRequest.Name} {leaveRequest.Num}天的请假申请");
                return;
            }
            this.NextHandler?.HandleRequest(leaveRequest);
        }
    }

    public class CEO : Handler
    {
        public CEO(string name) : base(name)
        {
        }

        public override void HandleRequest(LeaveRequest leaveRequest)
        {
            Console.WriteLine($"CEO:{this.Name} 审批了{leaveRequest.Name} {leaveRequest.Num}天的请假申请");
        }
    }

客户端调用:

csharp 复制代码
        static void Main(string[] args)
        {

            LeaveRequest leaveRequest = new LeaveRequest("张三",12,"事假");

            Handler projectLeader = new ProjectLeader("李四");
            Handler manager = new Manager("王二");
            Handler cEO = new CEO("赵六");
            projectLeader.NextHandler = manager;
            manager.NextHandler= cEO;

            projectLeader.HandleRequest(leaveRequest);

            Console.ReadKey();
        }

在这个代码示例中,我们定义了一个抽象处理者 Handler,并在其中定义了一个对下一个处理者的引用 NextHandler。然后,定义了三个具体处理者 ProjectLeader,Manager 和 CEO,并实现了 Handler 接口中的 HandleRequest 方法。在 HandleRequest 方法中,每个具体处理者对象都会根据自己的能力判断是否能够处理该请求,如果能够处理,则直接处理;否则将请求转发给下一个处理者对象。

4 优缺点分析

  • 优点:

    • 降低耦合度:降低了请求发送者和接收者之间的耦合。

    • 职责更清晰:把多个条件判定分散到各个处理类中,使得代码更加清晰,责任更加明确。

  • 缺点:

    • 在找到正确的处理对象之前,所有的条件判定都要执行一遍,当责任链过长时,可能会引起性能的问题。
    • 可能导致某个请求不被处理。
    • 客户端需要组装这个链条,耦合了客户端和链条的组成结构,可以把这个在客户端的组合动作提到外面,通过配置来做,会更好点。

结语

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
C#设计模式17------责任链模式的写法
C#设计模式系列:职责链模式(Chain of Responsibility)
C#设计模式(21)------责任链模式
C#设计模式之二十职责链模式(Chain of Responsibility Pattern)【行为型】
c#中责任链模式详解

相关推荐
WaaTong17 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_7430484417 分钟前
初识Java EE和Spring Boot
java·java-ee
AskHarries19 分钟前
Java字节码增强库ByteBuddy
java·后端
霁月风20 分钟前
设计模式——观察者模式
c++·观察者模式·设计模式
小灰灰__39 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭42 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果1 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林1 小时前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
duration~2 小时前
Maven随笔
java·maven