【设计模式】职责链模式

1、概述

在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假或者加薪,可处理的领导有HR、部门负责人、副总经理、总经理等,但每个领导能批准的天数和加薪额度不同,员工必须根据自己的情况去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这增加了难度。这样的例子还有很多,如找领导出差报销、生活中的“击鼓传花”游戏、发薪资等。

定义

又名责任链模式,为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

职责链模式主要包含以下角色:

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

2、实现思路

场景

现需要开发一个请假流程控制系统。请假一天以下的假只需要主管同意即可;请假2天需要项目经理同意;请假2天到4天的假还需要部门经理同意;请求4天到15天还需要总经理同意才行。

面向过程编程POP
            /// <summary>/// Program/// </summary>Application application = new Application(){Code = "Leave001",Description = "请假单20220715",Num = 16,Type = "请假单",IsApproval = false,};#region 面向过程编程POP{if (application.Num <= 8){Console.WriteLine("主管批准");}else if (application.Num <= 16){Console.WriteLine("项目经理批准");}else if (application.Num <= 32){Console.WriteLine("部门经理批准");}else{Console.WriteLine("总经理批准");}}#endregion

如果你的编程思想还是面向过程编程(POP)的思想,那么可能会写出上述代码。

但是我们要发挥C#面向对象编程(OOP)的特点,所以我们可以分别创建主管、项目经理、部门经理、总经理类,并在这些类中进行判断和流转。

面向对象编程OOP

首先,我们创建各个管理者类;

    /// <summary>/// 主管/// </summary>public class Director{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 8){Console.WriteLine("主管审批通过!");application.IsApproval = true;}}}/// <summary>/// 项目经理/// </summary>public class ProjectManager{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 16){Console.WriteLine("项目经理通过");application.IsApproval = true;}}}/// <summary>/// 部门经理/// </summary>public class DivisionManager{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 32){Console.WriteLine("部门经理审批通过");application.IsApproval = true;}}}/// <summary>/// 总经理/// </summary>public class President{public string Name { get; set; }public override void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");Console.WriteLine("总经理审批通过");application.IsApproval = true;}}

并通过Program类进行流转,

            /// <summary>/// Program/// </summary>#region 面向对象编程OOP{Director director = new Director();director.Name = "Tom";director.Approval(application);if (!application.IsApproval){ProjectManager projectManager = new ProjectManager();projectManager.Name = "Bob";projectManager.Approval(application);if (!application.IsApproval){DivisionManager divisionManager = new DivisionManager();divisionManager.Name = "Mark";divisionManager.Approval(application);if (!application.IsApproval){President president = new President();president.Name = "Alice";president.Approval(application);}}}}#endregion
===============================================Director-Tom Approval
ProjectManager-Bob Approval
DivisionManager-Mark Approval
部门经理审批通过

但是,这么写就把业务都暴露在了主方法内,我们需要把流转放入管理者类内部,使其自动流转。我们对各个管理者类修改如下,

审批自动流转
    /// <summary>/// 主管/// </summary>public class Director{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 8){Console.WriteLine("主管审批通过!");application.IsApproval = true;}else{Console.WriteLine("我没有权限,请找上级审批!");ProjectManager manager = new ProjectManager();manager.Approval(application);}}}/// <summary>/// 项目经理/// </summary>public class ProjectManager{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 16){Console.WriteLine("项目经理通过");application.IsApproval = true;}else{Console.WriteLine("我没有权限,请找上级审批!");DivisionManager manager = new DivisionManager();manager.Approval(application);}}}/// <summary>/// 部门经理/// </summary>public class DivisionManager{public string Name { get; set; }public void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 32){Console.WriteLine("部门经理审批通过");application.IsApproval = true;}else{Console.WriteLine("我没有权限,请找上级审批!");President manager = new President();manager.Approval(application);}}}

此时,Program类内部只需要如下代码,

         /// <summary>/// Program/// </summary>#region 审批自动流转{Director director = new Director();director.Name = "Tom";director.Approval(application);}#endregion
=====================================
Director-我是主管 Approval
我没有权限,请找上级审批!
ProjectManager- Approval
我没有权限,请找上级审批!
DivisionManager- Approval
部门经理审批通过

我们可以看出各个管理者类有着很多重复的代码,此时我们就可以把重复的代码抽离出来,放到一个抽象类里面。

抽象管理者类
    /// <summary>/// 抽象管理者类/// </summary>public abstract class AbstractManager{public string Name { get;set;}public abstract void Approval(Application application);}/// <summary>/// 主管/// </summary>public class Director : AbstractManager{public override void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 8){Console.WriteLine("主管审批通过!");application.IsApproval = true;}else{Console.WriteLine("我没有权限,请找上级审批!");ProjectManager manager = new ProjectManager();manager.Approval(application);}}}
......
责任链模式精髓

进一步地,当我们要在项目经理和部门经理类直接加一个流转,或者要把项目经理的流转放在主管前面时,我们就需要去修改各个管理者类的代码,这也就违背了开闭原则。所以,我们这里要使得流转的过程能够控制,另一位开发同事可以自主去修改流转。具体代码如下,

    public abstract class AbstractManager{public string Name { get;set;}protected AbstractManager _abstractManager = null;public void SetNext(AbstractManager abstractManager){this._abstractManager = abstractManager;}public abstract void Approval(Application application);protected void ApprovalNext(Application application){Console.WriteLine("我没有权限,请找上级审批!");if (this._abstractManager != null){this._abstractManager.Approval(application);}}}/// <summary>/// 主管/// </summary>public class Director : AbstractManager{public override void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 8){Console.WriteLine("主管审批通过!");application.IsApproval = true;}else{base.ApprovalNext(application);}}}/// <summary>/// 项目经理/// </summary>public class ProjectManager : AbstractManager{public override void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 16){Console.WriteLine("项目经理通过");application.IsApproval = true;}else{base.ApprovalNext(application);}}}/// <summary>/// 部门经理/// </summary>public class DivisionManager:AbstractManager{public override void Approval(Application application){Console.WriteLine($"{this.GetType().Name}-{this.Name} {nameof(Approval)}");if (application.Num < 32){Console.WriteLine("部门经理审批通过");application.IsApproval = true;}else{base.ApprovalNext(application);}}}/// <summary>/// Program/// </summary>#region 审批流程变化(责任链模式精髓){AbstractManager manager1 = new Director();manager1.Name = "Tom";AbstractManager manager2 = new ProjectManager();manager2.Name = "Bob";AbstractManager manager3 = new DivisionManager();manager3.Name = "Mark";AbstractManager manager4 = new President();manager4.Name = "Alice";manager1.SetNext(manager3);manager3.SetNext(manager4);manager1.Approval(application);}#endregion
==============================================================
Director-Tom Approval
我没有权限,请找上级审批!
DivisionManager-Mark Approval
部门经理审批通过

上述Program部分代码可以看出,我们能够将主管的流转改为部门经理。如果我们想要在项目经理之后加上大项目经理,我们也主要添加一个大项目经理类,然后在配置项目经理SetNext流转到大项目经理,不用去修改其他管理者类。

责任链模式+建造者模式

当然,我们如果不希望把业务流转的过程暴露在Program类中,我们可以引入建造者模型,在一个builder类中专门创建管理者类,并管理他们的流转方式。

    public class ManagerBuilder{public static AbstractManager Build(){AbstractManager manager1 = new Director();manager1.Name = "Tom";AbstractManager manager2 = new ProjectManager();manager2.Name = "Bob";AbstractManager manager3 = new DivisionManager();manager3.Name = "Mark";AbstractManager manager4 = new President();manager4.Name = "Alice";manager1.SetNext(manager3);manager3.SetNext(manager4);return manager1;}}/// <summary>/// Program/// </summary>#region 责任链模式+建造者模式{AbstractManager manager = ManagerBuilder.Build();manager.Approval(application);}#endregion
==============================================================
Director-Tom Approval
我没有权限,请找上级审批!
DivisionManager-Mark Approval
部门经理审批通过

3、优缺点

优点
  • 降低了对象之间的耦合度

    该模式降低了请求发送者和接收者的耦合度。

  • 增强了系统的可扩展性

    可以根据需要增加新的请求处理类,满足开闭原则。

  • 增强了给对象指派职责的灵活性

    当工作流程发生变化,可以动态地改变链内的成员或者修改它们的次序,也可动态地新增或者删除责任。

  • 责任链简化了对象之间的连接

    一个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。

  • 责任分担

    每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点
  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

【设计模式】职责链模式(C#)相关推荐

  1. Python设计模式-职责链模式

    Python设计模式-职责链模式 代码基于3.5.2,代码如下; #coding:utf-8 #职责链模式class Handler():def __init__(self):self.success ...

  2. 第二十章 Caché 设计模式 职责链模式

    文章目录 第二十章 Caché 设计模式 职责链模式 定义 优点 缺点 结构图 描述 完整示例 请求类 抽象责任类 实现责任类 调用 思考 第二十章 Caché 设计模式 职责链模式 定义 使多个对象 ...

  3. C++设计模式-职责链模式

    目录 基本概念 代码与实例 在哪种地方使用 基本概念 职责链模式(Chain of Responsibility):时多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这个对象连 ...

  4. JAVA 设计模式 职责链模式

    用途 职责链模式 (Chain Of Responsibility) 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系. 将这个对象连成一条链,并沿着这条链传递该请求,直到有一个 ...

  5. PHP设计模式——职责链模式

    声明:本系列博客参考资料<大话设计模式>,作者程杰. 职责链模式(又叫责任链模式)包含了一些命令对象和一些处理对象,每个处理对象决定它能处理那些命令对象,它也知道应该把自己不能处理的命令对 ...

  6. 设计模式|职责链模式--流程状态审批(枚举实现)

    流程审批 在实际的项目中,我们经常会涉及到某个对象实体的状态转换,比如合同审批,请假审批,订单流程状态流转,由于本文不涉及工作流(Activiti,Zeebe)的介绍,只是介绍职责链模式的设计模式,因 ...

  7. C++设计模式——职责链模式(responsibility chain pattern)

    一.原理讲解 1.1意图 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止. 1.2应用场景 有多个对象可以处 ...

  8. 设计模式---职责链模式(Chain of Responsibility Pattern)

    目录 1.学校 OA系统采购审批需求 2.传统方式解决审批流程 3.传统 方式 问题分析 4.职责链模式基本介绍 5.职责链模式原理类图 6.职责链模式解决OA采购审批 7.职责链模式在SpringM ...

  9. 设计模式-职责链模式

    1.定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这个对象连成一条链,并沿着这条链传递该请求,直到对象处理它为止.    相信大家都写过if...else或者swit ...

  10. 趣谈设计模式 | 职责链模式(ChainOfResposibility):请求的转发处理

    文章目录 案例:企业信息处理 职责链模式 总结 完整代码与文档 案例:企业信息处理 对于一个企业来说,每天难免的要收到大量的信息,有求职者发送过来的简历.其他公司的商务合作信息.又或者是和一些企业或客 ...

最新文章

  1. linux设备驱动第五篇:驱动中的并发与竟态
  2. 有哪些好的科研和学习习惯?
  3. 将jar文件转换成exe可执行文件
  4. 【JS】执行上下文(ExcecutionContext)
  5. php类和对象-作用,php 类和对象
  6. Java笔记-JDK搭建WebService客户端其他调用方法
  7. STM32之窗口看门狗例程
  8. rip协议中周期性广播路由信息的报文_关于RIP的一点小笔记--华为
  9. Feedforward Deep Networks(要点)
  10. Atitit 学习方法 补充 艾龙 著 attilax著 1. Atitit 学习的方法 attilax总结 1 1.1. 2. 基于学习策略的分类 2 2 1.2. 3. 基于所获取知识的表示形
  11. 转载:日本动漫编年史
  12. 软考网络工程师重难点总结分享~(3)
  13. setting配置文件详解
  14. 优秀ppt作品下载欣赏
  15. 分享一个本人打造的公众号吸粉、推广方案。
  16. springcloud搭建以及集成tx-lcn分布式事务解决框架
  17. Flutter ExpansionTile 折叠组件的使用
  18. 霍兰德air适合学计算机吗,霍兰德职业兴趣测验(含职业代码)
  19. 移动端UI界面设计之APP字体排版原则| 萧蕊冰
  20. Vision Transformer 必读系列之图像分类综述(二): Attention-based

热门文章

  1. 使用fui(Find Unused Imports)扫描工程中不用的类
  2. 奇偶模分析(Odd Even Mode Analysis)
  3. android 视频截屏黑屏,【报Bug】currentWebview.draw在安卓端绘制截图时,video组件中的视频内容无法绘制,截图后呈黑屏,IOS端可以正常绘制截屏...
  4. powerline字体安装
  5. ajax提交form表单方法
  6. “植物奶油”危害堪比苏丹红 欧美已经封杀叫停
  7. 软件测试投了几十份简历为什么没有面试邀约?
  8. Stable Diffusion教学 使用Lora制作AI网红 【AI绘画真人教程】
  9. 搜狗玩花样能否迎合年轻人?
  10. R语言使用cox函数构建生存分析回归模型、使用subgroupAnalysis进行亚组分析并可视化森林图