学习设计模式《二十二》——职责链模式
一、基础概念
职责链模式的本质是【分离职责,动态组合】。
分离职责是前提,只有先把复杂的功能分开,拆分成很多的步骤和小的功能处理,然后才能合理规划和定义职责类。可以有很多的职责类来负责处理某一个功能,让每个职责类负责处理功能的某一个方面,在运行期间进行动态组合,形成一个处理的链,把这个链运行完,功能也就处理完了。
动态组合才是职责链模式的精华所在,因为要实现请求对象和处理对象的解耦,请求对象不知道谁才是真正的处理对象,因此要动态地把可能得处理对象组合起来。由于组合的方式是动态的,这就意味着可以很方便地修改和添加新的处理对象,从而让系统更加灵活和具有更好的扩展性。
这么做还有一个潜在的优点:就是可以增强职责功能的复用性,如果职责功能是很多地方都可以使用的公共功能,那么它可以在很多职责链中复用。
职责链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
序号 | 认识职责链模式 | 说明 |
1 | 职责链模式的功能 | 职责链模式主要用来处理“客户端发出一个请求,让多个对象都有机会来处理这一个请求,但是客户端不知道究竟谁会来处理他的请求”这样的情况。也就是需要让请求者和接收者解耦。这样就可以动态地切换和组合接收者了。 【注意:在标准的职责链模式中,只要有对象处理了请求,这个请求就到此为止,不再被传递和处理了】。 如果要变形使用职责链,就可以让这个请求继续传递,每个职责对象对这个请求进行一定的功能处理,从而形成一个处理请求的功能链。 |
2 | 隐式接收者 | 当客户端发出请求的时候,客户端并不知道谁会真正处理他的请求,客户端只知道它提交的第一个对象。从第一个处理对象开始,整个职责链中的对象,要么自己处理请求,要么继续转发给下一个接收者。也就是对于请求而言,并不知道最终的接收者是谁,但是一般情况相爱,总是会有一个对象来处理的,因此称为隐式接收者。 |
3 | 如何构建链 | 职责链的链怎么构建呢?实现的方式有很多,归结起来大致有如下一些方式 1-首先按照实现的地方来说: 如果是从外部获取数据来构建链,那么在程序运行的时候,会读取这些数据,然后根据数据的要求来获取相应的对象,并组合起来。 |
4 | 谁来处理 | 职责链中那么多对象,到底谁来处理请求呢?这个是在运行时期动态决定的。当请求被传递到某个处理对象的时候,这个对象会按照已经设定好的条件来判断是否属于自己处理的范围,如果是就处理,如果不是就转发请求给下一个对象。 |
5 | 请求一定会被处理吗 | 在职责链模式中,请求不一定会被处理,因为可能没有合适的处理者,请求在职责链中从头传递到尾,每个处理对象都判断不属于自己处理,最后请求就没有对象来处理。这一点是需要注意的。可以在职责链的末端始终加上一个不支持此功能处理的职责对象,这样如果传递到这里,就会出现提示,本职责链没有对象处理这个请求。 |
功能链:在实际开发中,经常会遇到把职责链稍稍变形的用法【在标准的职责链中,一个请求在职责链中传递,只要有一个对象处理了这个请求,就会停止】。现在稍稍变通一下,改成一个请求在职责链中传递,每个职责对象负责处理请求某一个方面的功能,处理完成后,不是停止,而是继续向下传递请求,当请求通过很多职责对象处理后,功能也就完成了,这样的职责链称为功能链。
序号 | 职责链模式的优点 | 职责链模式的缺点 |
1 | 请求者和接收者松散耦合 在职责链模式中,请求者并不知道接收者是谁,也不知道具体如何处理,请求者只是负责向职责链发出请求就可以了。而每个职责对象也不用管请求者或者是其他的职责对象,只负责处理自己的部分,其他的就交给其他的职责对象去处理。也就是说,请求者和接收者是完全解耦的。 | 产生很多细粒度对象 职责链模式会把功能处理分散到单独的职责对象中,也就是每个职责对象只处理一个方面的功能,要把整个业务处理完,需要很多职责对象的组合,这样会产生大量的细粒度职责对象。 |
2 | 动态组合职责 职责链模式会把功能处理分散到单独的职责对象中,然后在使用的时候,可以动态组合职责形成职责链,从而可以灵活地给对象分配职责,也可以灵活地实现金和改变对象的职责。 | 不一定能被处理 职责链模式的每个职责对象只负责自己处理的那一部分,因此可能会出现某个请求,把整个链传递完了,都没有职责对象处理它。这就需要在使用职责链模式的时候,需要提供默认的处理,并且注意构建的链的有效性。 |
何时选用职责链模式?
《1》如果有很多对象可以处理同一个请求,但是具体由哪个对象来处理该请求,是运行时刻动态确定的。这种情况可以使用职责链模式,把处理请求的对象实现成为职责对象,然后把它们构成一个职责链,当请求在这个链中传递的时候,具体由哪个职责对象来处理,会在运行时动态判断。
《2》如果你想在不明确指定接收者的情况下,向多个对象中的其中一个提交请求的话,可以使用职责链模式。职责链模式实现了请求者和接收者之间的解耦,请求者不需要知道究竟是哪一个接收者对象来处理了请求。
《3》如果想要动态指定处理一个请求的对象集合,可以使用职责链模式。职责链模式能动态地构建职责链,也就是动态地来决定到底那些职责对象来参与到处理请求中来,相当于是动态地指定了处理一个请求的职责对象集合。
二、职责链模式示例
业务需求:很多公司都有这样的福利,就是项目组或者部门可以向公司申请一些聚餐费用,用于组织项目组成员或者部门成员进行聚餐活动,可以增进人员之间的情感,更有利于工作中的相互合作。申请聚餐的费用大致流程一般是:由申请人先填写申请单,然后交给领导审查,如果申请批准下来,领导会通知申请人审批通过,然后申请人去财务核领费用,如果没有核准,领导会通知申请人审批未通过,此事就此作罢。不同级别的领导,对于审批的额度是不一样的(如:项目经理只能审核500元以内的申请;部门经理可以审批1000元以内的申请;而总经理可以审核任意额度的申请)。
总结一下就是:当某人提出聚餐费用申请的请求后,该请求会由项目经理、部门经理、总经理之中的某一位领导来进行相应的处理,但是提出申请的人并不知道最终会由谁来处理他的请求,一般申请人都是把自己的申请提交给项目经理,或许是最后由总经理来处理他的请求,但是申请人并不知道应该由总经理来处理他的请求。那么该怎样实现这样的功能呢?
2.1、不使用模式的示例
其实,这个需求的主要逻辑是根据申请费用的多少,然后让不同的领导来进行处理就可以了:
2.1.1、实现申请聚餐费用逻辑
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.NoPattern
{/// <summary>/// 费用申请/// </summary>internal class FeeRequest{/// <summary>/// 提交聚餐费用申请给项目经理/// </summary>/// <param name="user">申请人</param>/// <param name="fee">申请费用</param>/// <returns></returns>public string RequestToProjectManager(string user, double fee){string str=string.Empty;if (fee < 500){//项目经理的权限比较小,只能在500以内str =ProjectManagerHandle(user,fee);}else if (fee < 1000){//部门经理的权限只能在1000以内str =DepmentManagerHandle(user,fee);}else if (fee >= 1000){//总经理权限较大,只要请求到了这里都可以处理str=GeneralManagerHandle(user,fee);}return str;}//项目经理审批费用private string ProjectManagerHandle(string user, double fee){string str = string.Empty;//为了测试简单只同意张三if ("张三".Equals(user)){str = $"项目经理同意【{user}】聚餐费用【{fee}】元的请求";}else{//其他人一律不同意str = $"项目经理不同意【{user}】聚餐费用【{fee}】元的请求";}return str;}//部门经理审批费用private string DepmentManagerHandle(string user, double fee){string str = string.Empty;//为了测试简单只同意张三if ("张三".Equals(user)){str = $"部门经理同意【{user}】聚餐费用【{fee}】元的请求";}else{//其他人一律不同意str = $"部门经理不同意【{user}】聚餐费用【{fee}】元的请求";}return str;}//总经理审批费用private string GeneralManagerHandle(string user, double fee){string str = string.Empty;//为了测试简单只同意张三if ("张三".Equals(user)){str = $"总经理同意【{user}】聚餐费用【{fee}】元的请求";}else{//其他人一律不同意str = $"总经理不同意【{user}】聚餐费用【{fee}】元的请求";}return str;}}//Class_end
}
2.1.2、客户端测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){NoPatternTest();Console.ReadLine();}/// <summary>/// 不用模式测试/// </summary>private static void NoPatternTest(){Console.WriteLine("---不用模式测试---");NoPattern.FeeRequest feeRequest = new NoPattern.FeeRequest();string res1 = feeRequest.RequestToProjectManager("张三",300);Console.WriteLine(res1);string res2 = feeRequest.RequestToProjectManager("李四",300);Console.WriteLine(res2);Console.WriteLine();string res3 = feeRequest.RequestToProjectManager("张三", 900);Console.WriteLine(res3);string res4 = feeRequest.RequestToProjectManager("李四", 900);Console.WriteLine(res4);Console.WriteLine();string res5 = feeRequest.RequestToProjectManager("张三", 1005);Console.WriteLine(res5);string res6 = feeRequest.RequestToProjectManager("李四", 1005);Console.WriteLine(res6);Console.WriteLine();}}//Class_end
}
2.1.3、运行结果
这个示例已经实现了需求的功能,但是仔细分析一下申请聚餐费用的业务功能和目前的实现,还存在如下几个问题:
《1》聚餐费用申请的处理流程是可能变动的(即:现在的处理流程是:提交给费用申请给项目经理,看是否适合项目经理审批,若不是则向下看是否适合部门经理审批,若不是则继续向下到总经理处理的步骤。但是今后这个流程可能会变化为:直接将费用申请提交给部门经理,看是否适合部门经理处理,若不是则向下到总经理处理的步骤。也就是说,对于聚餐费用的申请审批,要求处理的逻辑是灵活可变的)。
《2》各个处理环节的业务处理也是会变动的(即:业务处理流程可能发生变化,也会导致某些具体的业务功能发生变化。如:原本部门经理审批聚餐费用的时候,只是判断是否批准,现在,部门经理可能在审批聚餐费用的时候,核算本部门的实时成本,这就出现新的业务处理功能了)。
若采用上面这个示例的实现,一旦处理的逻辑发生了变化,解决的方法是:①生成一个子类,覆盖RequestToProjectManager方法,然后在里面实现新的处理;②另外一个方法就是修改处理申请方法的源代码来实现(要是具体处理环节的业务处理功能发生了变化,那就只好找到相应的处理方法,进行源代码修改)。这都不是什么好的方法,也就是说,如果出现聚餐费用申请的处理流程变化的情况或者是出现各个处理环节的功能编号的时候,上面的实现方法是很难灵活地编号来适应新要求的。
2.2、使用职责链模式示例1
将上面的问题都抽象出来(也就是说:客户端发出一个请求,会由很多的对象都可以来处理这个请求,而且不同对象的处理逻辑是不一样的。对于客户端来说,无所谓谁来处理,反正有对象处理就可以了。在上述处理中,还希望处理流程是可以灵活变动的,而处理请求的对象需要能方便地修改或者是被替换掉,以适应新的业务功能需要)该如何实现呢?
2.2.1、定义职责对象接口
在职责对象的抽象类中,定义所有职责的外观(在这个类中持有下一个处理请求的对象,同时还要定义业务处理方法):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoOne
{/// <summary>/// 职责对象接口/// </summary>abstract internal class Handler{/// <summary>/// 持有下一个处理请求的对象/// </summary>public Handler Successor { get; set; }/// <summary>/// 费用申请/// </summary>/// <param name="user">申请人</param>/// <param name="fee">申请的费用</param>/// <returns>返回申请结果</returns>public abstract string HandleRequest(string user,double fee);}//Class_end
}
2.2.2、实现各个角色的职责
现在的费用申请审批流程是:申请人提出申请提交给项目经理处理,项目经理的处理权限是500元以内,超过500元就把申请转给部门经理,部门经理的处理权限是1000元以内,超过1000元,就把申请转给总经理处理(可以看到,这个费用审批需要经过三个角色【项目经理、部门经理、总经理】审批处理,把这三个角色实现成为对应的角色对象,一个对象实现一个环节的处理功能)。
《1》项目经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoOne
{/// <summary>/// 项目经理/// </summary>internal class ProjectManager : Handler{public override string HandleRequest(string user, double fee){string str=string.Empty;//项目经理的权限比较小,只能在500以内if (fee < 500){//为了测试简单,只同意张三的请求if ("张三".Equals(user)){str = $"项目经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"项目经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//超过500继续传递给更高级别的人处理if (this.Successor!=null){return Successor.HandleRequest(user, fee);}}return str;}}//Class_end
}
《2》部门经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoOne
{/// <summary>/// 部门经理职责/// </summary>internal class DepmentManager : Handler{public override string HandleRequest(string user, double fee){string str = string.Empty;//部门经理的权限只能在1000以内if (fee < 1000){//为了测试简单只同意张三的申请if ("张三".Equals(user)){str = $"部门经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"部门经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//超过1000元,继续传递给更高级别的人处理if (this.Successor!=null){return this.Successor.HandleRequest(user, fee);}}return str;}}//Class_end
}
《3》总经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoOne
{/// <summary>/// 总经理职责/// </summary>internal class GeneralManager : Handler{public override string HandleRequest(string user, double fee){string str = string.Empty;//总经理权限很大,只要到了这里都可以处理if (fee >= 1000){//为了测试简单只同意张三的申请if ("张三".Equals(user)){str = $"总经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"总经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//若还有后续的处理对象,则继续传递if (this.Successor!=null){return this.Successor.HandleRequest(user, fee);}}return str;}}//Class_end
}
2.2.3、客户端构建职责链测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){ChainOfResposibilityDemoOneTest();Console.ReadLine();}/// <summary>/// 责任链示例1测试/// </summary>private static void ChainOfResposibilityDemoOneTest(){Console.WriteLine("------责任链示例1测试------");//组装职责链ChainOfResposibilityDemoOne.Handler handler1 = new ChainOfResposibilityDemoOne.ProjectManager();ChainOfResposibilityDemoOne.Handler handler2 = new ChainOfResposibilityDemoOne.DepmentManager();ChainOfResposibilityDemoOne.Handler handler3 = new ChainOfResposibilityDemoOne.GeneralManager();//指定项目经理的下一个对象是部门经理handler1.Successor = handler2;//指定部门经理的下一个对象是总经理handler2.Successor = handler3;//测试string res1 = handler1.HandleRequest("张三",300);Console.WriteLine(res1);string res2 = handler1.HandleRequest("李四",300);Console.WriteLine(res2);Console.WriteLine();string res3 = handler1.HandleRequest("张三", 900);Console.WriteLine(res3);string res4 = handler1.HandleRequest("李四", 900);Console.WriteLine(res4);Console.WriteLine();string res5 = handler1.HandleRequest("张三", 1005);Console.WriteLine(res5);string res6 = handler1.HandleRequest("李四", 1005);Console.WriteLine(res6);Console.WriteLine();}}//Class_end
}
2.2.4、运行结果
2.3、使用职责链模式示例2——处理多种请求
上面的使用职责链模式示例1都是同一个职责链处理一种请求的情况,现在有这样的需求实现预支差旅费用,假设还是同一流程,也就是组合同一个职责链(从项目经理-->部门经理-->总经理)虽然流程相同,但是每个处理类都需要处理两种请求,它们的具体业务逻辑是不一样的,那么该如何实现呢?预支费用流程中在5000以内项目经理可以审批,在10000以内部门经理可以审批,在10000以上直接到总经理审批(这里预支差旅费用属于工作需要,就直接同意了)。
简单的处理方式:就是为每种业务单独定义一个方法,然后在客户端根据不同的需要调用不同的方法。
2.3.1、定义职责对象
在这里改造职责对象的接口,添加上新的业务方法(新增预支差旅费方法):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoTwo
{/// <summary>/// 职责对象接口/// </summary>abstract internal class Handler{/// <summary>/// 下一个处理请求对象/// </summary>public Handler Successor { get; set; }/// <summary>/// 费用申请/// </summary>/// <param name="user">申请人</param>/// <param name="fee">申请的费用</param>/// <returns>返回申请结果</returns>public abstract string HandleFeeRequest(string user,double fee);/// <summary>/// 预支费用申请/// </summary>/// <param name="user">申请人</param>/// <param name="fee">预支费用</param>/// <returns>true表示同意</returns>public abstract bool HandlePreFeeRequest(string user,double fee);}//Class_end
}
2.3.2、实现各个角色的职责
既然职责接口发生了改变,对应各个角色的职责处理类也要改变(同样都是判断一下是否属于自己的处理范围,如果属于自己处理的范围就处理,否则就传递到下一个处理)。
《1》项目经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoTwo
{/// <summary>/// 项目经理职责/// </summary>internal class ProjectManager : Handler{public override string HandleFeeRequest(string user, double fee){string str = string.Empty;//项目经理的权限比较小,只能在500以内if (fee < 500){//为了测试简单,只同意张三的请求if ("张三".Equals(user)){str = $"项目经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"项目经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//超过500继续传递给更高级别的人处理if (this.Successor != null){return Successor.HandleFeeRequest(user, fee);}}return str;}public override bool HandlePreFeeRequest(string user, double fee){string str= string.Empty;//项目经理权限较小,只能在5000以内if (fee < 5000){str = $"项目经理同意【{user}】预支费用【{fee}】元的请求";Console.WriteLine(str);return true;}else{//超过5000,继续传递个更高级别的人处理if (Successor!=null){return Successor.HandlePreFeeRequest(user,fee);}}return false;}}//Class_end
}
《2》部门经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoTwo
{/// <summary>/// 部门经理职责/// </summary>internal class DepmentManager : Handler{public override string HandleFeeRequest(string user, double fee){string str = string.Empty;//部门经理的权限只能在1000以内if (fee < 1000){//为了测试简单只同意张三的申请if ("张三".Equals(user)){str = $"部门经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"部门经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//超过1000元,继续传递给更高级别的人处理if (Successor != null){return Successor.HandleFeeRequest(user, fee);}}return str;}public override bool HandlePreFeeRequest(string user, double fee){string str = string.Empty;//部门经理的权限只能在10000以内if (fee < 10000){str = $"部门经理同意【{user}】预支费用【{fee}】元的请求";Console.WriteLine(str);return true;}else{//超过10000,继续传递个更高级别的人处理if (Successor != null){return Successor.HandlePreFeeRequest(user, fee);}}return false;}}//Class_end
}
《3》总经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoTwo
{/// <summary>/// 总经理职责/// </summary>internal class GeneralManager : Handler{public override string HandleFeeRequest(string user, double fee){string str = string.Empty;//总经理权限很大,只要到了这里都可以处理if (fee >= 1000){//为了测试简单只同意张三的申请if ("张三".Equals(user)){str = $"总经理同意【{user}】聚餐费用【{fee}】元的请求";}else{str = $"总经理不同意【{user}】聚餐费用【{fee}】元的请求";}}else{//若还有后续的处理对象,则继续传递if (Successor != null){return Successor.HandleFeeRequest(user, fee);}}return str;}public override bool HandlePreFeeRequest(string user, double fee){string str = string.Empty;//总经理权限很大,只要到了这里都可以处理if (fee >= 10000){str = $"总经理同意【{user}】预支费用【{fee}】元的请求";Console.WriteLine(str);return true;}else{//若还有后续的处理对象,则继续传递if (Successor != null){return Successor.HandlePreFeeRequest(user, fee);}}return false;}}//Class_end
}
2.3.3、客户端构建职责链测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){ChainOfResposibilityDemoTwoTest();Console.ReadLine();}/// <summary>/// 责任链示例2测试/// </summary>private static void ChainOfResposibilityDemoTwoTest(){Console.WriteLine("------责任链示例2测试------");//先组装责任链ChainOfResposibilityDemoTwo.Handler handler1 = new ChainOfResposibilityDemoTwo.ProjectManager();ChainOfResposibilityDemoTwo.Handler handler2 = new ChainOfResposibilityDemoTwo.DepmentManager();ChainOfResposibilityDemoTwo.Handler handler3 = new ChainOfResposibilityDemoTwo.GeneralManager();//指定项目经理的下一个对象是部门经理handler1.Successor = handler2;//指定部门经理的下一个对象是总经理handler2.Successor = handler3;//开始测试申请费用string res1 = handler1.HandleFeeRequest("张三",300);Console.WriteLine(res1);string res2 = handler1.HandleFeeRequest("张三", 900);Console.WriteLine(res2);string res3 = handler1.HandleFeeRequest("张三", 1005);Console.WriteLine(res3);Console.WriteLine();//开始测试预支费用handler1.HandlePreFeeRequest("张三",4500);handler1.HandlePreFeeRequest("张三",9999);handler1.HandlePreFeeRequest("张三",16000);}}//Class_end
}
2.3.4、运行结果
这个示例的实现看起来很容易,我们仔细分析观察一下,这样的实现有没有问题?(这种实现方式有一个明显的问题就是:只要增加一个业务,就需要修改职责的接口,很不灵活;且接口一改,需要修改的地方很多,频繁修改接口绝对不是一个好方法)那么有没有什么好方法来实现呢?
2.4、使用职责链模式示例3——灵活扩展
分析一下现在变化的内容:
《1》不同的业务需要传递的业务数据不同;
《2》不同的业务请求的方法不同;
《3》不同的职责对象处理这些不同的业务请求的逻辑不同。
现在有一种简单的方式,可以较好地解决这这些问题:①定义一套通用的调用框架,用一个通用的请求对象来封装请求传递的参数;②定义一个通用的调用方法,这个方法不去区分具体的业务,所有业务都是这一个方法,那么具体的业务如何区分?就是在通用的请求对象中会有一个业务标记:到了职责对象中,愿意处理就和原来使用一样的处理方式;不愿意处理,就传递到下一个处理对象就可以了。对于返回值也可以来个通用的,最简单的就是使用object类型。
为了示范,先假定只有一个业务方法,等把这一个业务方法搞定了,在扩展另外的业务方法,这样就可以清晰地看出这种设计的好处了。
2.4.1、先定义通用的请求对象模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 通用的请求对象/// </summary>internal class RequestModel{//具体的业务类型private string type=string.Empty;/// <summary>/// 构造方法吧具体的业务类型传递进来/// </summary>/// <param name="type">业务类型</param>public RequestModel(string type){this.type = type;}/// <summary>/// 获取到当前类型/// </summary>/// <returns></returns>public string GetCurType{get { return this.type; }}}//Class_end
}
2.4.2、定义职责对象
在职责对象里面除了持有下一个对象、还需要对处理请求的方法进行修改为通用的:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 通用职责对象/// </summary>abstract internal class Handler{/// <summary>/// 持有下一个处理请求的对象/// </summary>public Handler Successor { get; set; }/// <summary>/// 通用处理请求的方法/// </summary>/// <param name="rm">请求对象</param>/// <returns></returns>public virtual object HandleRequest(RequestModel rm){if (Successor != null){//这是默认实现(若子类不处理这个请求)那就传递到下一个职责对象去处理return this.Successor.HandleRequest(rm);}else{Console.WriteLine("没有后续处理或暂时不支持这样的功能处理");return false;}}}//Class_end
}
2.4.3、实现第一个业务(聚餐费用申请)
《0》定义聚餐费用的对象模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 费用申请业务相关数据模型/// </summary>internal class FeeRequestModel:RequestModel{/// <summary>/// 约定具体的业务类型/// </summary>public readonly static string FEE_TYPE = "fee";public FeeRequestModel() : base(FEE_TYPE){}/// <summary>/// 申请人/// </summary>public string User { get; set; }/// <summary>/// 申请金额/// </summary>public double Fee { get; set; }}//Class_end
}
《1》项目经理职责对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 项目经理职责/// </summary>internal class ProjectManager:Handler{/// <summary>/// 覆盖通用处理方法,按照业务类型调用自己的处理方法/// </summary>/// <param name="rm">请求对象</param>/// <returns></returns>public override object HandleRequest(RequestModel rm){if (FeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的费用申请return HandleFeeRequest(rm);}else{//其他内容暂不处理使用默认逻辑return base.HandleRequest(rm);}}/// <summary>/// 处理费用申请/// </summary>/// <param name="rm">请求类型</param>/// <returns></returns>private object HandleFeeRequest(RequestModel rm){//先把通用的对象显式转换回来FeeRequestModel feeRequestModel = (FeeRequestModel)rm;string str=string.Empty;//项目经理的权限较小,只能在500以内if (feeRequestModel.Fee < 500){//为了测试简单,只同意张三的请求if ("张三".Equals(feeRequestModel.User)){str = $"项目经理同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}else{//其他人一律不同意str = $"项目经理不同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}}else{//超过500的,继续传递给更高级别的人处理if (base.Successor!=null){return Successor.HandleRequest(rm);}}return str;}}//Class_end
}
《2》部门经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 部门经理职责/// </summary>internal class DepmentManager:Handler{/// <summary>/// 覆盖通用处理方法,按照业务类型调用自己的处理方法/// </summary>/// <param name="rm">请求对象</param>/// <returns></returns>public override object HandleRequest(RequestModel rm){if (FeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的费用申请return HandleFeeRequest(rm);}else{//其他内容暂不处理使用默认逻辑return base.HandleRequest(rm);}}//处理费用申请private object HandleFeeRequest(RequestModel rm){//先把通用的对象显式转换回来FeeRequestModel feeRequestModel = (FeeRequestModel)rm;string str=string.Empty;//部门经理的权限只能在1000以内if (feeRequestModel.Fee < 1000){//为了测试简单只同意张三的申请if ("张三".Equals(feeRequestModel.User)){str = $"部门经理同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}else{str = $"部门经理不同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}}else{//超过1000的,继续传递给更高级别的人处理if (base.Successor != null){return Successor.HandleRequest(rm);}}return str;}}//Class_end
}
《3》总经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 总经理职责/// </summary>internal class GeneralManager:Handler{public override object HandleRequest(RequestModel rm){if (FeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的费用申请return HandleFeeRequest(rm);}else{//其他内容暂不处理使用默认逻辑return base.HandleRequest(rm);}}//处理费用申请private object HandleFeeRequest(RequestModel rm){//先把通用的对象显式转换回来FeeRequestModel feeRequestModel = (FeeRequestModel)rm;string str = string.Empty;//总经理权限很大,只要到了这里都可以处理if (feeRequestModel.Fee >= 1000){//为了测试简单只同意张三的申请if ("张三".Equals(feeRequestModel.User)){str = $"总经理同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}else{str = $"总经理不同意【{feeRequestModel.User}】聚餐费用【{feeRequestModel.Fee}】元的请求";}}else{//若还有后续处理对象,则继续传递if (base.Successor!=null){return base.Successor.HandleRequest(rm);}}return str;}}//Class_end
}
2.4.4、客户端构建职责链测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){ChainOfResposibilityDemoThreeTest();Console.ReadLine();}/// <summary>/// 责任链示例3测试/// </summary>private static void ChainOfResposibilityDemoThreeTest(){Console.WriteLine("------责任链示例3测试------");//组装责任链ChainOfResposibilityDemoThree.Handler handler1 = new ChainOfResposibilityDemoThree.ProjectManager();ChainOfResposibilityDemoThree.Handler handler2 = new ChainOfResposibilityDemoThree.DepmentManager();ChainOfResposibilityDemoThree.Handler handler3 = new ChainOfResposibilityDemoThree.GeneralManager();//指定项目经理的下一个对象是部门经理handler1.Successor = handler2;//指定部门经理的下一个对象是总经理handler2.Successor= handler3;//开始测试费用申请ChainOfResposibilityDemoThree.FeeRequestModel feeRequestModel = new ChainOfResposibilityDemoThree.FeeRequestModel();feeRequestModel.User = "张三";feeRequestModel.Fee = 300;//调用处理请求string res1 = (string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res1);Console.WriteLine();//重设金额在调用处理feeRequestModel.Fee = 900;string res2=(string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res2);Console.WriteLine();//重设金额在调用处理feeRequestModel.Fee = 1005;string res3 = (string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res3);Console.WriteLine();}}//Class_end
}
2.4.5、运行结果
2.4.6、扩展新业务(预支差旅费)
《0》定义预支差旅费的对象模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 预支费用申请模型/// </summary>internal class PreFeeRequestModel : RequestModel{/// <summary>/// 约定具体的业务类型/// </summary>public readonly static string FEE_TYPE="preFee";public PreFeeRequestModel() : base(FEE_TYPE){}/// <summary>/// 申请人/// </summary>public string User { get; set; }/// <summary>/// 申请金额/// </summary>public double Fee { get; set; }}//Class_end
}
《1》项目经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 项目经理职责扩展/// 现在既可以处理一般的费用申请,还可以处理预支费用/// </summary>internal class ProjectManagerExtand : ProjectManager{/// <summary>/// 覆盖通用处理方法,按照业务类型调用自己的处理方法/// </summary>/// <param name="rm">请求对象</param>/// <returns></returns>public override object HandleRequest(RequestModel rm){if (PreFeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的预支费用申请return HandlePreFeeRequest(rm);}else{//其他内容暂不处理使用父类逻辑return base.HandleRequest(rm);}}/// <summary>/// 处理预支费用申请/// </summary>/// <param name="rm">请求类型</param>/// <returns></returns>private object HandlePreFeeRequest(RequestModel rm){//先把通用的对象显式转换回来PreFeeRequestModel preFeeRequestModel = (PreFeeRequestModel)rm;//项目经理的权限较小,只能在5000以内if (preFeeRequestModel.Fee < 5000){//工作需要,全部同意string str = $"项目经理同意【{preFeeRequestModel.User}】聚餐费用【{preFeeRequestModel.Fee}】元的请求";Console.WriteLine(str);return true;}else{//超过5000的,继续传递给更高级别的人处理if (base.Successor!=null){return Successor.HandleRequest(rm);}}return false;}}//Class_end
}
《2》部门经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 部门经理职责拓展/// </summary>internal class DepmentManagerExtand : DepmentManager{/// <summary>/// 覆盖通用处理方法,按照业务类型调用自己的处理方法/// </summary>/// <param name="rm">请求对象</param>/// <returns></returns>public override object HandleRequest(RequestModel rm){if (PreFeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的预支费用申请return HandlePreFeeRequest(rm);}else{//其他内容暂不处理使用默认逻辑return base.HandleRequest(rm);}}//处理预支费用申请private object HandlePreFeeRequest(RequestModel rm){//先把通用的对象显式转换回来PreFeeRequestModel preFeeRequestModel = (PreFeeRequestModel)rm;//部门经理的权限只能在10000以内if (preFeeRequestModel.Fee < 10000){//工作需要,全部同意string str = $"部门经理同意【{preFeeRequestModel.User}】预支费用【{preFeeRequestModel.Fee}】元的请求";Console.WriteLine(str);return true;}else{//超过10000的,继续传递给更高级别的人处理if (base.Successor != null){return Successor.HandleRequest(rm);}}return false;}}//Class_end
}
《3》总经理职责
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoThree
{/// <summary>/// 总经理职责拓展/// </summary>internal class GeneralManagerExtand : GeneralManager{public override object HandleRequest(RequestModel rm){if (PreFeeRequestModel.FEE_TYPE.Equals(rm.GetCurType)){//返回处理的费用申请return HandlePreFeeRequest(rm);}else{//其他内容暂不处理使用默认逻辑return base.HandleRequest(rm);}}//处理费用申请private object HandlePreFeeRequest(RequestModel rm){//先把通用的对象显式转换回来PreFeeRequestModel preFeeRequestModel = (PreFeeRequestModel)rm;//总经理权限很大,只要到了这里都可以处理if (preFeeRequestModel.Fee >= 10000){//工作需要全部同意string str = $"总经理同意【{preFeeRequestModel.User}】预支费用【{preFeeRequestModel.Fee}】元的请求";Console.WriteLine(str);return true;}else{//若还有后续处理对象,则继续传递if (base.Successor!=null){return base.Successor.HandleRequest(rm);}}return false;}}//Class_end
}
2.4.7、客户端构建职责链测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){ChainOfResposibilityDemoThreeExtandTest();Console.ReadLine();}/// <summary>/// 责任链示例3拓展测试/// </summary>private static void ChainOfResposibilityDemoThreeExtandTest(){Console.WriteLine("------责任链示例3拓展测试------");//组装责任链ChainOfResposibilityDemoThree.Handler handler1 = new ChainOfResposibilityDemoThree.ProjectManagerExtand();ChainOfResposibilityDemoThree.Handler handler2 = new ChainOfResposibilityDemoThree.DepmentManagerExtand();ChainOfResposibilityDemoThree.Handler handler3 = new ChainOfResposibilityDemoThree.GeneralManagerExtand();//指定项目经理的下一个对象是部门经理handler1.Successor = handler2;//指定部门经理的下一个对象是总经理handler2.Successor = handler3;//开始测试费用申请ChainOfResposibilityDemoThree.FeeRequestModel feeRequestModel = new ChainOfResposibilityDemoThree.FeeRequestModel();feeRequestModel.User = "张三";feeRequestModel.Fee = 300;//调用处理请求string res1 = (string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res1);Console.WriteLine();//重设金额在调用处理feeRequestModel.Fee = 900;string res2 = (string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res2);Console.WriteLine();//重设金额在调用处理feeRequestModel.Fee = 1005;string res3 = (string)handler1.HandleRequest(feeRequestModel);Console.WriteLine(res3);Console.WriteLine();//开始测试预支费用ChainOfResposibilityDemoThree.PreFeeRequestModel preFeeRequestModel = new ChainOfResposibilityDemoThree.PreFeeRequestModel();preFeeRequestModel.User = "张三";preFeeRequestModel.Fee = 4500;//调用处理请求handler1.HandleRequest(preFeeRequestModel);//重新设置预支费用preFeeRequestModel.Fee = 9999;handler1.HandleRequest(preFeeRequestModel);//重新设置预支费用preFeeRequestModel.Fee = 10001;handler1.HandleRequest(preFeeRequestModel);}}//Class_end
}
2.4.8、运行结果
到这里可以看到这种通用设计的好处:既通用又灵活,有了新的业务,只需要添加新的实现新功能的对象就可以了。但是带来的缺陷可能就是会造成对象层次过多,或者出现较多的细粒度的对象。极端情况下,每次扩展一个方法,就会出现大量只处理一个功能的细粒度对象。
2.5、使用职责链模式示例4——功能链
在实际的应用开发中,进行业务处理之前,通过需要进行权限检查、通用数据校验、数据逻辑校验等处理,然后才开始真正的业务逻辑实现。可以将这些功能分散到一个功能链中。这样做的目的是使程序结构更加灵活,而且复用性会更好(如:通用的权限检查只需要做一份,然后就可以在多个功能链中使用了)。
业务需求:实现商品销售的业务处理,在真正进行销售业务处理之前,需要对传入的数据进行权限检查、通用数据和数据逻辑检查,只有这些检查都能通过的情况下,才说明传入的数据是正确的、有效的,才可以进行真正的业务功能处理。
2.5.1、定义销售数据模型
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 销售模型/// </summary>internal class SaleModel{/// <summary>/// 销售商品/// </summary>public string GoodsName { get; set; }=string.Empty;/// <summary>/// 销售数量/// </summary>public int SaleNumber { get; set; } = 0;public override string ToString(){string str = $"商品名称【{GoodsName}】销售数量【{SaleNumber}】";return str ;}}//Class_end
}
2.5.2、定义销售抽象接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 销售职责接口/// </summary>abstract internal class SaleHandler{/// <summary>/// 持有下一个处理请求的对象/// </summary>public SaleHandler Successor { get; set; } = null;/// <summary>/// 处理保存销售信息的请求/// </summary>/// <param name="user">操作人员</param>/// <param name="customer">客户</param>/// <param name="saleModel">销售对象</param>/// <returns>true:表示成功</returns>public abstract bool Sale(string user,string customer,SaleModel saleModel);}//Class_end
}
2.5.3、定义各个职责对象
《1》权限检查职责对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 权限检查对象/// </summary>internal class SaleSecurityCheck : SaleHandler{public override bool Sale(string user, string customer, SaleModel saleModel){//进行权限检查,为了演示方便就只让张三通过if ("张三".Equals(user)){return base.Successor.Sale(user, customer, saleModel);}else{Console.WriteLine($"抱歉,【{user}】你没有保存销售信息的权限");return false;}}}//Class_end
}
《2》数据通用检查职责对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 进行数据通用检查的职责对象/// </summary>internal class SaleDataCheck : SaleHandler{public override bool Sale(string user, string customer, SaleModel saleModel){//数据通用检查if (string.IsNullOrEmpty(user)){Console.WriteLine("申请人不能为空");return false;}if (string.IsNullOrEmpty(customer)){Console.WriteLine("客户不能为空");return false;}if (saleModel==null){Console.WriteLine("销售商品的数据不能为空");return false;}if (string.IsNullOrEmpty(saleModel.GoodsName)){Console.WriteLine("销售的商品不能为空");return false;}if (saleModel.SaleNumber<=0){Console.WriteLine("销售商品的数量不能小于等于0");return false;}//如果通过了以上的检测,就继续向下继续执行return base.Successor.Sale(user, customer, saleModel);}}//Class_end
}
《3》数据逻辑检查职责对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 进行数据的逻辑检查的职责对象/// </summary>internal class SaleLogicCheck : SaleHandler{public override bool Sale(string user, string customer, SaleModel saleModel){//进行数据的逻辑检查(如:检查ID的唯一性,主外键的对应关系等)//这里应该检查这种主外键的对应关系(如:销售商品是否存在)//我们这里为了演示就直接通过//通过上面的检测,那就向下继续执行return base.Successor.Sale(user, customer, saleModel);}}//Class_end
}
《4》真正销售业务职责对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 真正处理销售业务功能的职责对象/// </summary>internal class SaleMgr : SaleHandler{public override bool Sale(string user, string customer, SaleModel saleModel){//进行真正的业务逻辑处理Console.WriteLine($"【{user}】保存了【{customer}】购买的【{saleModel}】销售数据");return true;}}//Class_end
}
《5》构建销售功能链对象
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ChainOfResposibilityPattern.ChainOfResposibilityDemoFour
{/// <summary>/// 商品销售管理模块处理/// </summary>internal class GoodsSaleHandle{//保存销售信息(本来销售数据应该是多条的,我们这里主要是演示职责链内容简化了)public bool Sale(string user,string customer,SaleModel saleModel){/*若全部功能在这里处理,基本顺序如下*///1-权限检查//2-通用数据检查//3-数据逻辑校验//4-真正的业务处理/*但现在这些功能是通过功能链来实现,这里就只用构建功能链即可*/SaleSecurityCheck ssc= new SaleSecurityCheck();SaleDataCheck sdc= new SaleDataCheck();SaleLogicCheck slc= new SaleLogicCheck();SaleMgr sm= new SaleMgr();ssc.Successor = sdc;sdc.Successor = slc;slc.Successor = sm;//向链上的第一个对象发出处理请求return ssc.Sale(user,customer,saleModel);}}//Class_end
}
2.5.4、客户端测试
namespace ChainOfResposibilityPattern
{internal class Program{static void Main(string[] args){ChainOfResposibilityDemoFourTest();Console.ReadLine();}/// <summary>/// 责任链示例4测试/// </summary>private static void ChainOfResposibilityDemoFourTest(){Console.WriteLine("------责任链示例4测试------");//创建业务对象ChainOfResposibilityDemoFour.GoodsSaleHandle goodsSaleHandle = new ChainOfResposibilityDemoFour.GoodsSaleHandle();//准备测试数据ChainOfResposibilityDemoFour.SaleModel saleModel = new ChainOfResposibilityDemoFour.SaleModel();saleModel.GoodsName = "周杰伦-七里香";saleModel.SaleNumber = 16;//调用业务功能goodsSaleHandle.Sale("张三","周茜",saleModel);goodsSaleHandle.Sale("李四","李倩",saleModel);}}//Class_end
}
2.5.5、运行结果
三、项目源码工程
kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern