通俗易懂设计模式解析——责任链模式
前言
今天我们介绍的是责任链模式【chain of responsibility pattern】。对于责任链模式理解起来还是比较容易的。例如在公司请假、三天以内部门经理批准即可,但是三到七天可能就需要总监批准了、七天以上需要副总裁批准。对于这么一个需求最初的解决方案就是if-else语句判断。但是 一旦请假的模式增加一种则需要对多重if-else进行修改,这就违背了开闭原则。这个时候就可以采用责任链模式来解决其问题。责任链模式为请求创建一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。
责任链模式介绍
一、来由
在软件系统中,经常会有一个请求可能会被多个对象处理。但是每一次都是被一个对象处理。又不能确定是哪一个对象。如果显示指定每一个对象。会对请求发送者和接收者造成紧耦合。那么如何做到对请求的发送者和接收者进行解耦。并且在运行时自行决定处理请求的对象呢?
二、意图
避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
三、案例
四、责任链模式代码示例
看上述案例图,主要涉及到两个部分:
抽象处理者:定义一个处理请求的接口
具体处理者:实现处理请求的接口、可以选择是自己处理或者传递给下一个接收者。包含对下一个接收处理者的引用。
责任链模式的组成部分还是比较简单的。我们看这么一个案例,还是用户结算时金额计算的案例。根据用户的会员等级进行对应的折扣结算。普通用户全额计算、普通会员95折计算、黄金会员9折计算、钻石会员7折计算:
我们首先看看不使用责任链模式如何处理:
namespace chainofresponsibility_pattern { class program { static void main(string[] args) { decimal money = 200m; var membertype = membertype.goldmember; //普通会员,95折计算 if (membertype == membertype.member) { console.writeline($"普通会员,95折计算,最后金额为{money * 0.95m}"); } //黄金会员,9折计算 else if (membertype == membertype.goldmember) { console.writeline($"黄金会员,9折计算,最后金额为{money * 0.9m}"); } //钻石会员,7折计算 else if (membertype == membertype.diamondsmember) { console.writeline($"钻石会员,7折计算,最后金额为{money * 0.7m}"); } //无会员,全额计算 else { console.writeline($"无会员,全额计算,最后金额为{money}"); } } public enum membertype { [description("无会员")] nomember = 1, [description("普通会员")] member = 2, [description("黄金会员")] goldmember = 3, [description("钻石会员")] diamondsmember = 4 } } }
这里我们可以看到我们使用了多个if条件判断完成的此需求(或者switch语句)。这里我们增加一种会员方式或者优惠折扣都是直接对条件语句进行修改的。或者某个会员的折扣进行了修改都需要对条件进行修改。这里请求的发送者和接收者具有紧耦合。并且多个if条件加在一起,不易于判断及理解。
我们再看看使用责任链模式:
namespace chainofresponsibility_pattern { public enum membertype { [description("无会员")] nomember=1, [description("普通会员")] member =2, [description("黄金会员")] goldmember =3, [description("钻石会员")] diamondsmember =4 } class chainofresponsibilitypattern { } /// <summary> /// 结算请求 /// </summary> public class settlementrequest { /// <summary> /// 金额 /// </summary> public decimal _money; /// <summary> /// 会员类型 /// </summary> public membertype _membertype; public settlementrequest(decimal money,membertype membertype) { this._money = money; this._membertype = membertype; } } /// <summary> /// 结算抽象处理 /// </summary> public abstract class settlementhandler { /// <summary> /// 下一位接收处理者 /// </summary> public settlementhandler nexthandler; public abstract void settlement(settlementrequest settlementrequest); } /// <summary> /// 无会员接收者 /// </summary> public class nomemberhandler : settlementhandler { public override void settlement(settlementrequest settlementrequest) { if (settlementrequest._membertype==membertype.nomember) { console.writeline($"无会员,不进行折扣计算。最后金额为{settlementrequest._money}"); } else { nexthandler.settlement(settlementrequest); } } } /// <summary> /// 普通会员接收处理者 /// </summary> public class memberhandler : settlementhandler { public override void settlement(settlementrequest settlementrequest) { if (settlementrequest._membertype == membertype.member) { console.writeline($"普通会员,95折计算。最后金额为{settlementrequest._money*0.9m}"); } else { nexthandler.settlement(settlementrequest); } } } /// <summary> /// 黄金会员接收处理者 /// </summary> public class goldmemberhandler : settlementhandler { public override void settlement(settlementrequest settlementrequest) { if (settlementrequest._membertype == membertype.goldmember) { console.writeline($"黄金会员,9折计算。最后金额为{settlementrequest._money*0.9m}"); } else { nexthandler.settlement(settlementrequest); } } } /// <summary> /// 钻石会员接收处理者 /// </summary> public class diamondsmemberhandler : settlementhandler { public override void settlement(settlementrequest settlementrequest) { if (settlementrequest._membertype == membertype.diamondsmember) { console.writeline($"钻石会员,7折计算。最后金额为{settlementrequest._money*0.7m}"); } else { nexthandler.settlement(settlementrequest); } } } }
namespace chainofresponsibility_pattern { class program { static void main(string[] args) { ///设置请求 settlementrequest settlementrequest = new settlementrequest(200, membertype.goldmember); ///初始化具体处理 settlementhandler nomember = new nomemberhandler(); settlementhandler member = new memberhandler(); settlementhandler goldmember = new goldmemberhandler(); settlementhandler diamondsmember = new diamondsmemberhandler(); ///设置责任链 nomember.nexthandler = member; member.nexthandler = goldmember; goldmember.nexthandler = diamondsmember; ///处理请求 nomember.settlement(settlementrequest); } } }
这里我们将会员判断及计算细化到了不同的类中。每一个类都仅针对他自己的会员折扣进行计算。我们增加会员模式也仅需要增加一个具体处理类并且重建责任链即可。如果修改某个会员的对应折扣也仅修改对应的类即可。我们看下面的运行结果和上面的也是一样的。
使用场景及优缺点
一、使用场景
1、有多个对象处理同一个请求,具体哪一个对象处理在运行时自行确定
2、在不明白具体接收者的情况下,向多个对象中的一个提交请求
3、代码块中存在多个if-else语句的情况下,可以考虑使用责任链模式进行重构
4、一个系统的审批需要多个对象才能完成处理。例如请假系统或者采购模块。
二、优点
1、降低请求发送者和接收者之间的耦合度
2、简化了对象、使对象不清除链的结构
3、增加了对象指派职责的灵活度、增强了可扩展性
4、将多个条件语句进行分散到各个具体处理类中,增加代码的可阅读性。使代码更加清晰,责任更明确
三、缺点
1、在未找到请求接收者之前会对多有对象执行一遍,如果责任链过长会对性能造成一定影响
2、可能导致某一个请求不被处理
3、可能不太容易进行错误排查
4、容易造成循环调用
总结
责任链模式主要降低了请求发送者和接收者之间的耦合度。使得多个对象都有机会处理某一个请求。在请假系统中可能需要多个人物对请假进行审批。采购系统中可能需要不同高度的人进行审批。一个请求可能由多个对象处理,但是不清楚具体接收者对象。所以将对象连成一条链子,使请求沿着这条链子传递下去。直到被处理为止、这就是责任链模式。
即使受伤了,也要抬起头微笑着说,今天天气真好。
欢迎大家扫描下方二维码,和我一起踏上设计模式的闯关之路吧!