模式定义:
当一个请求从客户端发出后,请求以 链条 的形式 顺序的传递给 每一个处理者 进行处理;这便是责任链模式; 每个处理者 只负责处理自己负责的部分,然后决定将请求 传递下去 或者 中断请求;
并且 请求 可以选择责任链中 任何一个 处理者 进行处理,然后传递给后续处理者; 并且 责任链 中 处理者顺序 可以根据需求灵活定义;
生活中的例子
- 请假审批流程; 从 员工 到 小组组长 到 部门经理 到 总经理 到.....; 每个处理者 负责处理自己的部分,并将 请假请求传给下个 处理者 或者 也可以 拒绝请假 并 中断 审批流程; 这种责任链 传递过的每个处理者必须都要处理任务;
- 公司员工 排队体检流程; 男医生 检查 男员工,女医生检查 女员工, 一个员工 不可能 男女医生都检查; 每个处理者 完全负责 全部责任,要么处理,要么 传递给下个处理者; 这种责任链 只有一个处理者能处理,然后停止传递;
代码中的例子(何时该使用此模式):
- 用户 登录 接口; 包括 如下校验: IP地址 是否出国 ; 用户是否被封禁; 用户密码是否正确 等等; 也许 多个系统 都是用的 同一个 用户体系,但是 每个系统 对 登录 校验项数量 和 校验顺序 不同; 这时 可以使用 责任链模式; 每个系统 定制 不同的 责任链; 这样实现了 责任间 代码的解耦,灵活定制;
该模式关键的角色:
- 处理者角色 (Handler) 声明了所有具体处理者的通用接口。 该接口通常仅包含单个方法用于请求处理, 但有时其还会包含一个设置链上下个处理者的方法。
-
基础处理者 (Base Handler) 是一个可选的类, 你可以将所有处理者共用的样本代码放置在其中。通常情况下, 该类中定义了一个保存对于下个处理者引用的成员变量。 客户端可通过将处理者传递给上个处理者的构造函数或设定方法来创建链。 该类还可以实现默认的处理行为: 确定下个处理者存在后再将请求传递给它。
-
具体处理者 (Concrete Handlers) 包含处理请求的实际代码。 每个处理者接收到请求后, 都必须决定是否进行处理, 以及是否沿着链传递请求。处理者通常是独立且不可变的, 需要通过构造函数一次性地获得所有必要地数据。
该模式的主要优缺点如下:
优点:
- 设计责任链时 安排处理者 顺序灵活,完全由 客户端根据需求定制;
- 符合开闭原则;可以在不更改现有代码的情况下,新增处理者,增加了 系统的可扩展性
- 单一职责原则;每个处理者 只负责自己的职责;
- 将 原本耦合在一起的 职责 解耦成不同的 处理者;并且 根据不同需求 进行顺序搭配;
- 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
缺点:
- 不保证 请求 进入责任链后一定被处理, 可能出现 请求 不满足 任何一个 处理者 的 条件
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
和 其他模式 的 比较:
- 责任链通常和组合模式结合使用。 在这种情况下, 叶组件接收到请求后, 可以将请求沿包含全体父组件的链一直传递至对象树的底部(叶节点找它爸,它爷,一直到根节点); 也就是 比如上面的 请假审批流程中,是根据 公司组织架构进行审批,那就是一个 树结构,一个公司多个部门,一个部门多个 小组 等等;
示例代码部分
# -*- coding: utf-8 -*-
"""
(C) Rgc
All rights reserved
create time '2020/11/27 16:54'
Usage:
通过 责任链模式 实现 提款机功能
用户 输入 正整数金额后,提取对应金额的 货币
责任链 中的 处理者包括 买入金额校验处理,100元处理,50元处理,20元处理,10元处理,5元处理,1元处理
规则为 优先使用大面值进行提现: 188元为 100元+50元+20元+10元+5元+3个1元
此责任链为 除了 金额校验 可以直接停止任务链外,每个处理者均要处理任务,直到任务链结束;
"""
from abc import ABC, abstractmethod
class Cash(object):
"""
提现请求类
请求数据,此处用 类进行灵活表示
"""
def __init__(self, amount):
"""
:param amount: 用户提现金额
"""
self.amount = amount
# 提现后 用户手中的所有面值
self.cash_list = []
# 剩余待提现金额, 如 180元在 100元处理者 处理后 则 此值为 80元
self.remain_amount = self.amount
class Handler(ABC):
"""
处理者角色
只负责声明 所有 具体处理者的通用方法;基本包括 处理请求的方法 和 设置 下个处理者的方法
"""
@abstractmethod
def handler(self, request: Cash):
"""
处理 请求
:param request:
:return:
"""
pass
@abstractmethod
def set_next(self, request):
"""
设置 下个处理者
:param request:
:return:
"""
pass
def __str__(self):
return self.__name__
class BaseHandler(Handler):
"""
基础处理者 角色
定义 保存下个处理者的 变量
定义 每个 子类(具体处理者) 公共方法的具体实现
定义 处理者角色 方法 的实现
"""
# 类变量,定义为 指定下个处理者
_next_handler: Handler = None
# 面值
face_amount = None
@abstractmethod
def handler(self, request: Cash):
"""
判断 下个处理者是否存在则 让下个处理者处理请求 否则任务链结束
:param request:
:return:
"""
# 下个处理者必须存在
if self._next_handler:
return self._next_handler.handler(request)
else:
print('没有下个处理者,此责任链结束')
return None
def set_next(self, handler: Handler) -> Handler:
"""
设置 下个处理者,并 返回下个处理者,从而 在代码形式上 形成链式定义处理者
:param handler:
:return:
"""
self._next_handler = handler
return handler
def cash_action(self, request):
"""
提现操作
:param request:
:return:
"""
remain_amount = request.remain_amount
# 面值 数量(有几个100元)
face_amount_count = remain_amount // self.face_amount
# 剩余待提现金额
request.remain_amount = remain_amount - face_amount_count * self.face_amount
# 把 几个100元面值 放到 cash_list
request.cash_list += [self.face_amount for _ in range(face_amount_count)]
print(f'提现 {face_amount_count}张{self.face_amount}元面值,剩余待提现{request.remain_amount}元')
class CashAmountHandler(BaseHandler):
"""
具体处理者角色
判断用户 输入金额是否合法(>0)
金额校验类
"""
def handler(self, request: Cash):
"""
如果用户 为正整数 则将请求传递给下个处理者,否则 直接取消此任务链
:param request:
:return:
"""
print(f'校验 用户输入提现金额:{request.amount}元')
if request.amount > 0 and isinstance(request.amount, int):
return super(CashAmountHandler, self).handler(request)
else:
print('用户金额输入错误,任务链结束')
class CashResultCheckHandler(BaseHandler):
"""
具体处理者角色
提现结果校验类
"""
def handler(self, request: Cash):
"""
校验 提现到手金额 和 输入的提现金额是否相同
:param request:
:return:
"""
if request.amount != sum(request.cash_list):
print(f'校验提现结果,提现{request.amount}元失败!!!,提现到手现金为:{request.cash_list}')
return super(CashResultCheckHandler, self).handler(request)
print(f'校验提现结果,提现{request.amount}元成功,提现到手现金为:{request.cash_list}')
class Cash100Handler(BaseHandler):
"""
具体处理者角色
在 处理者方法中 实现具体的 业务逻辑,并判断 是否将请求传递到下个处理者
提现100元类
"""
# 面值
face_amount = 100
def handler(self, request: Cash):
"""
具体处理者 方法
尽可能 给用户100元面值的现金,然后 将请求传递给下个处理者
:param request:
:return:
"""
self.cash_action(request)
return super(Cash100Handler, self).handler(request)
class Cash50Handler(BaseHandler):
"""
具体处理者角色
提现50元类
"""
# 面值
face_amount = 50
def handler(self, request: Cash):
"""
具体处理者 方法
:param request:
:return:
"""
self.cash_action(request)
return super(Cash50Handler, self).handler(request)
class Cash20Handler(BaseHandler):
"""
具体处理者角色
提现20元类
"""
# 面值
face_amount = 20
def handler(self, request: Cash):
"""
具体处理者 方法
:param request:
:return:
"""
self.cash_action(request)
return super(Cash20Handler, self).handler(request)
class Cash10Handler(BaseHandler):
"""
具体处理者角色
提现10元类
"""
# 面值
face_amount = 10
def handler(self, request: Cash):
"""
具体处理者 方法
:param request:
:return:
"""
self.cash_action(request)
return super(Cash10Handler, self).handler(request)
class Cash5Handler(BaseHandler):
"""
具体处理者角色
提现5元类
"""
# 面值
face_amount = 5
def handler(self, request: Cash):
"""
具体处理者 方法
:param request:
:return:
"""
self.cash_action(request)
return super(Cash5Handler, self).handler(request)
class Cash1Handler(BaseHandler):
"""
具体处理者角色
提现1元类
"""
# 面值
face_amount = 1
def handler(self, request: Cash):
"""
具体处理者 方法
:param request:
:return:
"""
self.cash_action(request)
return super(Cash1Handler, self).handler(request)
def cash_chain(amount):
"""
定义 提现 责任链;可以 客户端灵活定义 也可以 让提供服务的责任链端 定义,毕竟只有责任链编写者最了解代码
此处 由 责任链端定义
:return:
"""
# 将 用户提现请求 转为对象
cash = Cash(int(amount))
# 生成每个处理者 对象
cash_amount_obj = CashAmountHandler()
cash_100_obj = Cash100Handler()
cash_50_obj = Cash50Handler()
cash_20_obj = Cash20Handler()
cash_10_obj = Cash10Handler()
cash_5_obj = Cash5Handler()
cash_1_obj = Cash1Handler()
cash_result_check_obj = CashResultCheckHandler()
# 定义任务链
cash_amount_obj.set_next(cash_100_obj).set_next(cash_50_obj).set_next(cash_20_obj).set_next(cash_10_obj).set_next(
cash_5_obj).set_next(cash_1_obj).set_next(cash_result_check_obj)
# 将请求 交给某个 处理者,然后 处理者 自动将请求传递给下个处理者 从而走完任务链
cash_amount_obj.handler(cash)
if __name__ == '__main__':
while True:
amount = input('请输入提现金额:\n')
cash_chain(amount)
运行结果如下:
总结:
责任链模式 能够很好的 将 耦合在一起的 业务上 相似的 功能进行解耦,然后 客户端可以进行灵活定义责任链,从而增加系统可扩展性;
将一堆 if else 根据责任链模式 进行 灵活定义,从而重构成 优雅的代码;
将一堆 功能类似的 业务逻辑 解耦成 单独的对象,并且可以灵活 组织功能;
相关链接:
http://c.biancheng.net/view/1383.html
https://refactoringguru.cn/design-patterns/chain-of-responsibility