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#中责任链模式详解

相关推荐
哪 吒4 小时前
最简单的设计模式,抽象工厂模式,是否属于过度设计?
设计模式·抽象工厂模式
Theodore_10224 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
冰帝海岸5 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了5 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·6 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic6 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it6 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
懒洋洋大魔王6 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康6 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud