Python设计模式之责任链模式

责任链模式

1. 责任链模式

通俗点讲,责任链是一个链表结构,这条链上有多个节点,它们有相同的接口,各自有各自的责任和实现。当有数据输入时,第一个节点看自己能否处理问题,如果可以就进行处理,如果不能就数据交给下一个节点进行处理,以此类推直到最后一个责任节点。

上面这段描述里,我们可以总结出以下几个关键信息

  1. 链表
  2. 相同的接口
  3. 节点有各自的任务
  4. 从第一个节点开始处理输入,如果不能处理,交给下一个节点

为了实现相同的接口,在责任链模式中需要一个抽象处理者角色来定义一个抽象接口,责任链上的节点是具体处理者角色,处理者必须实现由抽象处理者定义的抽象接口并各自实现具体功能。

2. 责任链模式示例

在网站上设置密码时,对密码通常有如下要求:

  1. 密码长度大于6
  2. 包含小写字母和大写字母
  3. 包含特殊字符

我就以检查密码是否合法为例子讲解如何实现责任链模式,当然,实践中这样的检查是不需要使用责任链来实现的,只是这个例子更通俗易懂一些。

2.1 抽象处理者角色

from abc import ABC, abstractmethod


class AbcCheckHandler(ABC):
    @abstractmethod
    def check_password(self, password):
        pass

    @abstractmethod
    def set_next(self, handler):
        pass

AbcCheckHandler 类是抽象处理者角色,它存在的意义是定义具体处理者角色必须实现的接口,这样处理者角色在使用时才能规范行为。

2.2 具体处理者角色

具体处理者角色是抽象处理者角色的子类

class CheckHanlderBase(AbcCheckHandler):
    _next_handler = None
    def set_next(self, handler):
        self._next_handler = handler

    @abstractmethod
    def _check(self, password):
        pass

    def check_password(self, password):
        status, msg = self._check(password)
        if status:
            if self._next_handler:
                return self._next_handler.check_password(password)
            else:
                return True, ''
        else:
            return status, msg

class LengthCheckHandler(CheckHanlderBase):
    """
    专门检查长度
    """

    def _check(self, password):
        if len(password) > 6:
            return True, ''
        return False, '长度错误'


class CaseCheckHandler(CheckHanlderBase):
    """
    专门检查大小写
    """

    def _check(self, password):
        has_lower = False
        has_upper = False

        for letter in password:
            if 'a' < letter < 'z':
                has_lower = True
            if 'A' < letter < 'Z':
                has_upper = True

        if has_lower and has_upper:
            return True, ''
        else:
            return False, '大小写不符合要求'


class SpecialCheckHandler(CheckHanlderBase):
    def _check(self, password):
        for letter in ('!', '@', '#', '$'):
            if letter in password:
                return True, ''
        return False, '未包含特殊字符'


def check_password(password):
    len_checker = LengthCheckHandler()
    case_checker = CaseCheckHandler()
    special_checker = SpecialCheckHandler()

    len_checker.set_next(case_checker)
    case_checker.set_next(special_checker)

    status, msg = len_checker.check_password(password)
    if status:
        print(f"{password} 检查通过")
    else:
        print(f"{password} 检查不通过: {msg}")

LengthCheckHandler, CaseCheckHandler, SpecialCheckHandler这3个类是具体处理者角色,他们是CheckHanlderBase的子类,而CheckHanlderBase 是AbcCheckHandler的子类,我特地增加了CheckHanlderBase,为的是让代码更加简洁,你可以去掉它,让三个检查类直接继承AbcCheckHandler,但那样很多代码都会重复。

2.3 调用责任链

def get_password_checker():
    len_checker = LengthCheckHandler()
    case_checker = CaseCheckHandler()
    special_checker = SpecialCheckHandler()

    len_checker.set_next(case_checker)
    case_checker.set_next(special_checker)
    return len_checker

def check_password(password):
    password_checker = get_password_checker()
    status, msg = password_checker.check_password(password)
    if status:
        print(f"{password} 检查通过")
    else:
        print(f"{password} 检查不通过: {msg}")


if __name__ == "__main__":
    check_password("1324sfED!")
    check_password("323!")
    check_password("323df3!")
    check_password("323DKS3!")
    check_password("skdjOWRJ")

对于责任链的调用者,你不必关心链上有多少个节点,节点的顺序是什么,这些都是在创建责任链时决定的,由创建者负责,你只需要将数据输入给责任链即可。

3. 责任链模式的优缺点

优点:

  1. 增加或者删除责任很方便
  2. 降低耦合度,对于请求的发送者,无需关心接收者都有哪些,顺序如何

缺点:

  1. 可能会在程序调试方面制造一些麻烦,但可以通过合理的日志输出来规避

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案