设计模式|【手写源码-设计模式15】-责任链模式-基于人事请假单工作流场景

1:主题拆解 【设计模式|【手写源码-设计模式15】-责任链模式-基于人事请假单工作流场景】①基本介绍
②人事请假单工作流模拟
③责任链模式的优缺点
④适用场景
⑤应用实例
⑥ASP.NET 管道模型
2:基本介绍 设计模式|【手写源码-设计模式15】-责任链模式-基于人事请假单工作流场景
文章图片

责任链模式很像异常的捕获和处理,当一个问题发生的时候,当前对象看一下自己是否能够处理,不能的话将问题抛给自己的上级去处理,但是要注意这里的上级不一定指的是继承关系的父类,这点和异常的处理是不一样的。
所以可以这样说,当问题不能解决的时候,将问题交给另一个对象去处理,就这样一直传递下去直至当前对象找不到下线了,处理结束。
责任链可以是一条直线,一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。链上的每一个对象都是请求处理者,责任链模式可以将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行处理,客户端无须关系请求的处理细节以及具体的传递,只需要将请求发送到链上即可,实现请求发送者以及请求处理者的解耦。
3:人事请假单工作流模拟 今天我们通过源码模拟人事请假单工作流功能的实现来区分不同程序员对功能实现的风格。
需求:某一职员想请假10天,即80小时。
规则:8小时之内项目经理审核,16小时之内主管审核,32小时之内经理审核,64小时之内总监审核,128小时之内CEO审核。
咱们废话不多说,开始撸码
请假上下文

public class ApplyContext { public int Id { get; set; } public string Name { get; set; } /// /// 请假时长 /// public int Hour { get; set; } /// /// 请假描述 /// public string Description { get; set; } /// /// 审批结果,默认是false /// public bool AuditResult { get; set; } /// /// 审批备注 /// public string AuditRemark { get; set; } }

1:初级程序员
Console.WriteLine("这里是项目经理 {0} 审批", "fool"); if (context.Hour <= 8) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { Console.WriteLine("这里是主管 {0} 审批", "fool"); if (context.Hour <= 16) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { Console.WriteLine("这里是经理 {0} 审批", "fool"); if (context.Hour <= 32) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { Console.WriteLine("这里是总监 {0} 审批", "fool"); if (context.Hour <= 64) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { Console.WriteLine("这里是CEO {0} 审批", "fool"); if (context.Hour <= 128) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { context.AuditResult = false; context.AuditRemark = "reject!"; } } } } }

分析:
代码翻译机,需求怎么交代我们就怎么翻译,完全没有自己的加工。
把全部的业务逻辑都暴露在上端,面向过程式编程。
2:中级程序员 定义PM,主管,经理,总监,CEO对象
①对象抽象基类
public abstract class AbstractAuditor { public string Name { get; set; } public abstract void Audit(ApplyContext context); }

②各种对象实例类
public class PM : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是项目经理 {0} 审批", this.Name); if (context.Hour <= 8) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else {} } } public class Charge: AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是主管 {0} 审批", this.Name); if (context.Hour <= 16) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else {} } } public class Manager : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是经理 {0} 审批", this.Name); if (context.Hour <= 32) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { } } } public class Chif : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是总监 {0} 审批", this.Name); if (context.Hour <= 64) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { } } } public class CEO : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是CEO {0} 审批", this.Name); if (context.Hour <= 128) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { } } }

③上端调用
AbstractAuditor pm = new PM() { Name = "a" }; pm.Audit(context); if (!context.AuditResult) { AbstractAuditor charge = new Charge() { Name = "b" }; charge.Audit(context); if (!context.AuditResult) { AbstractAuditor manager = new Manager() { Name = "c" }; manager.Audit(context); if (!context.AuditResult) { AbstractAuditor chif = new Chif() { Name = "d" }; chif.Audit(context); if (!context.AuditResult) { AbstractAuditor ceo = new CEO() { Name = "e" }; ceo.Audit(context); } } } }

分析: 初步有了oop思想,考虑对象,转移了审批逻辑。
请假条+主管+经理---封装业务逻辑,继承复用代码,还有多态 。
但是也是个代码搬运工,也是个翻译机+OOP思想,因为没有自己的思考
这只是翻译,表面上正确,实际上不可用的代码,因为不可能越级请假
开发不仅是搬运,还得有思想。
3:高级程序员 ①实例类改造
public class PM : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是项目经理 {0} 审批", this.Name); if (context.Hour <= 8) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { AbstractAuditor charge = new Charge() { Name = "b" }; charge.Audit(context); } } } public class Charge: AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是主管 {0} 审批", this.Name); if (context.Hour <= 16) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { AbstractAuditor manager = new Manager() { Name = "c" }; manager.Audit(context); } } } public class Manager : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是经理 {0} 审批", this.Name); if (context.Hour <= 32) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { AbstractAuditor chif = new Chif() { Name = "d" }; chif.Audit(context); } } } public class Chif : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是总监 {0} 审批", this.Name); if (context.Hour <= 64) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { AbstractAuditor ceo = new CEO() { Name = "e" }; ceo.Audit(context); } } } public class CEO : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是CEO {0} 审批", this.Name); if (context.Hour <= 128) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else {} } }

②上端调用
AbstractAuditor pm = new PM() { Name = "a" }; pm.Audit(context);

分析:
程序员开始有思考就进入高级了。申请应该是自动流转,对象的职责要增加。
转移了申请提交逻辑。
但是假如审批流程变化了,组织架构发生变化,需要修改代码,缺乏前瞻性。
4:优秀的高级程序员 ①审批者抽象父类添加设置下个流程节点的职责
public abstract class AbstractAuditor { public string Name { get; set; } public abstract void Audit(ApplyContext context); private AbstractAuditor _NextAuditor = null; public void SetNext(AbstractAuditor auditor) { this._NextAuditor = auditor; } protected void AuditNext(ApplyContext context) { if (this._NextAuditor != null) { this._NextAuditor.Audit(context); } } }

②对象实例类改造
public class PM : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是项目经理 {0} 审批", this.Name); if (context.Hour <= 8) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { base.AuditNext(context); } } } public class Charge: AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是主管 {0} 审批", this.Name); if (context.Hour <= 16) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { base.AuditNext(context); } } } public class Manager : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是经理 {0} 审批", this.Name); if (context.Hour <= 32) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { base.AuditNext(context); } } } public class Chif : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是总监 {0} 审批", this.Name); if (context.Hour <= 64) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { base.AuditNext(context); } } } public class CEO : AbstractAuditor { public override void Audit(ApplyContext context) { Console.WriteLine("这里是CEO {0} 审批", this.Name); if (context.Hour <= 128) { context.AuditResult = true; context.AuditRemark = "enjoy your vacation!"; } else { base.AuditNext(context); } } }

③上端调用指定流程节点
AbstractAuditor pm = new PM() { Name = "a" }; AbstractAuditor charge = new Charge() { Name = "b" }; AbstractAuditor manager = new Manager() { Name = "c" }; AbstractAuditor chif = new Chif() { Name = "d" }; AbstractAuditor ceo = new CEO() { Name = "e" }; pm.SetNext(charge); charge.SetNext(manager); manager.SetNext(chif); chif.SetNext(ceo); pm.Audit(context);

分析:流程就可以变化了,能更好的应对业务的升级变迁,遵循开闭原则。
如果觉得上面设置流程部分负责,可以封装一下,采用反射+配置文件实现扩展。
5:架构师 架构师首先是有着丰富的程序设计经验,然后还需要能融汇贯通,能高度抽象业务,然后恰如其分的去应用各种程序设计思想,解决的问题不再是面向具体业务,而是面向框架。
4:责任链模式的优缺点 1:优点 ①降低耦合
职责链模式使得一个对象无须知道是其他哪一个对象处理请求,对象仅需知道请求会被处理即可,接收者和发送者都没有对方明确信息,且链中对象不需要知道链的结构,由客户端负责链的创建,降低了系统耦合度。
②简化对象连接
请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有候选处理者的引用,可简化对象的相互连接。
③灵活的职责链
可以在运行时对链进行动态增加或者修改。
④符合OCP
系统增加一个新的具体处理者时无须修改源码,只需要客户端重建职责链,符合OCP。
2:缺点 ①请求可能得不到处理
由于一个请求没有明确的接收者,因此请求不一定会被处理,也有可能因为职责链配置错误而得不到处理。
②性能受到影响
对于较长的职责链,请求的处理可能涉及多个处理对象,系统性能会受到一定影响,而且代码调试时可能不方便。
③死循环
如果职责链不当,可能会导致死循环调用。
5:适应场景 ①有多个对象可以处理同一个请求,具体哪个对象处理该请求待运行时刻再确定,客户端只需将请求提交到链上,而无须关心请求的处理对象是谁以及它是如何处理的。
②在不明确指定接收者的情况下,向多个对象的一个提交一个请求。
③可动态指定一组对象处理请求,客户端可以动态创建职责链来处理请求,还可以改变链中处理请求以及处理者之间的先后次序。
6:应用实例 人事行政审核
财务请款审核
7:ASP.NET 管道模型 设计模式|【手写源码-设计模式15】-责任链模式-基于人事请假单工作流场景
文章图片

请求进入ASP.NET 工作进程后,由进程创建HttpWorkRequest 对象,封装此次请求有关的所有信息,然后进入HttpRuntime 类进行进一步的处理。HttpRuntime 通过请求信息创建HttpContext 上下文对象,此对象将贯穿整个管道,直到响应结束。同时创建或从应用程序池里初始化一个HttpApplication对象,由此对象开始处理之前注册的多个HttpModule。之后调用HandlerFactory 创建Handler处理程序,最终处理此次请求内容,生存响应返回。

    推荐阅读