首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >原创 | 从抽象类开始,详解责任链模式

原创 | 从抽象类开始,详解责任链模式

作者头像
TechFlow-承志
发布于 2020-09-14 07:28:51
发布于 2020-09-14 07:28:51
58300
代码可运行
举报
文章被收录于专栏:TechFlowTechFlow
运行总次数:0
代码可运行

大家好,欢迎大家阅读周五的设计模式专题。

今天我们继续介绍新的设计模式,和上次的链式模式不同,这一次要介绍的责任链模式不仅仅在Python当中有,在很多其他的语言当中同样支持,比如Java。Python和Java当中的这个设计模式是完全一样的,没有任何区别。

和之前介绍的设计模式相比,今天介绍的设计模式同样不难。我们非常容易想明白它的原理。

责任链

在我们日常的开发当中,经常会有上下游的依赖,比如当前我们需要开发一份新的数据,需要用到上游的某一份老数据才能生成。这时候需要我们写一个接口,然后上游也随之改造,把它们的新数据写到我们这个接口来。

这就带来了一个问题,也就是上下游耦合的问题,如果下游有一天变动了,上游也需要随着变动。如果是一对一的可能还好,如果一个上游存在多个下游,那么这会是一件很麻烦的事。每一个下游变动了,上游都需要调整。举个最简单的例子,比如下游想要做一个AB测试,想要一半流量进入A机器,一半流量进入B机器。这都需要上游配合。

这当然是很不合理的,在实际当中上游的数据可能是另外一个团队负责的,他们只对数据质量负责,并不一定会愿意配合。并且这样的配合也会导致效率低下。

为了解决这个问题,有了责任链的设计模式。我们把下游的响应方存入一条链路当中,上游在发送请求时不感知下游的接收情况以及相应细节。

说白了,用一句话来概括,就是用链路把下游串起来。我们来看一张图就明白了:

我们把图中的采购人员看成是数据上游的话,它只管提出请求,具体响应的人他是不知道的。根据申请金额的不同,审批的人员也会不一样。但是只会有一个人审批,如果当前人员审批不了就会上报,也就是交由后面一个节点处理。不得不说是非常形象了。

代码实现

那怎么把下游用一条链路串起来呢,其实也很简单,我们可以利用抽象类来实现。比如说我们可以这样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Handler:
    def __init__(self, next=None):
        self.next = next
        
    def handle(self, request):
        res = self.do_handle(request)
        if not res and self.next:
            self.next.handle(request)
            
 def do_handle(self, request):
        pass

这里的handler就是实现链式调用的关键,在handle当中会首先自己进行处理,如果处理失败那么会调用下一个handle来进行。Handle类中do_handle的方法是抽象方法,我们希望每一个继承Handle的子类都能实现自己的do_handle逻辑。

所以如果是用Java来实现的话,会非常清晰,因为在Java当中有抽象类的相关概念。由于Python原生没有抽象类的相关设定,所以会稍微隐晦一些,但是逻辑是也一样的。

在我们代码实现之前,我们先来介绍一个无关紧要的包。这个包就是Python当中的six,其实这个包也不算是无关紧要,通过它可以写出兼容Python2和Python3的代码。但是实际上由于现在Python2已经快扫进历史的垃圾堆了,所以有没有这个包都没有关系,这里我们还是尊重原作者的代码,保留了这么一个包。我们用这个包作为注解,完成了抽象基类的构建。

抽象基类

首先,我们先来看这个抽象基类。在原生Python当中,其实是没有抽象基类这么一个概念的。抽象基类其实就是含有抽象方法的基类。

我们之前在介绍golang当中interface用法的时候曾经介绍过,我们来简单回忆一下。拿比较常见的Java语言举例。

比如说这是一个抽象基类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract class Mammal {
    abstract void say();
    String name() {
        return "chengzhi";
    }
}

对于这个类而言它是不可以直接创建实例的,因为我们可以看出来它有一个什么也没实现的方法say。这个方法前面加了一个关键字abstract即抽象的意思,表示这是一个抽象方法。类名的前面同样加了这个关键字,表示这是一个抽象类。

对于抽象类我们不能直接创建它的实例,我们只能创建实现了抽象类中抽象方法的子类的实例。

比如我们创建一个类实现它:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Human extends Mammal{
    @override
 public void say() {
  System.out.println("perfect");
 }
}

我们可以看出来Human这个类继承了Mammal这个抽象类,并且实现了抽象类当中的方法say。所以我们可以创建它的实例。抽象类是实现多态的一种重要的方法,在强变量类型的语言当中,我们通过抽象类抽象出了多个子类共同的结构。这样我们就可以通过父类的指针调用各种子类的对象了,这是非常方便的。

但是Python当中不支持,为什么Python不支持呢?其实也很简单,因为Python是弱变量类型语言。变量赋值的时候对于类型根本没有限制,我们可以做任何调用。

举个例子,比如我们当下有A、B和C这三个类的实例。哪怕这三个类毫无关系,我们也可以用一个变量去分别接收这些实例然后调用同名的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
a = [A(), B(), C()]
for i in a:
    i.say()

只要A、B和C这三个类当中都有叫做say的方法,那么上面这段逻辑就不会报错。因此Python非常灵活,可以说是几乎没有限制,那么当然也就没有必要设计这么一个抽象类的设定了。

但问题是我们开发的时候并不是这样的,比如说我们在设计的时候希望能够让类之间有继承的关系,并且希望继承了某个父类的类都能实现某一个方法。由于Python原生没有相关的设定,导致我们很难在编码的排查错误。比如说我们忘记实现了某个方法,由于Python没有编译的过程,我们只有运行到报错的地方才会遇到问题,这显然是不行的,会有很大的隐患。

所以Python开发者想了一个办法就是设计了一个装饰器,叫做abc。这里的abc不是随意起的名字,其实是abstract basement class的缩写。加上了这个装饰器之后,它会替我们去检查所有子类有没有实现抽象方法。如果没有的话,那么会报错提示我们。

我们用上six和abc这两个包之后实现出来的基类是这样的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import abc
import six


@six.add_metaclass(abc.ABCMeta)
class Handler(object):

    def __init__(self, successor=None):
        self.successor = successor

    def handle(self, request):
        res = self.check_range(request)
        if not res and self.successor:
            self.successor.handle(request)

    # 抽象方法
    @abc.abstractmethod
    def check_range(self, request):
        pass

实现类

我们把抽象类相关的概念以及责任链的原理理解了之后,就可以很简单地写出来实现类了。

所谓的实现类也就是抽象类的子类,实现了各自的抽象方法。这样我们在每个节点当中调用self.successor.handle的时候才可以执行。因为每一个successor都是继承了同样一个抽象类的实现类,它们都必然实现了handle这个抽象方法。所以整个调用的过程就像是链条一样串联了起来。

在这个例子当中,我们用数字表示每个handler的处理范围。只有落在范围里的请求才会被响应,如果当前handler无法响应,那么就会调用successor的handle继续尝试。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ConcreteHandler(Handler):

    @staticmethod
    def check_range(request):
        if 0 <= request < 10:
            print('request {} handled in handler 0'.format(request))
            return True

    
class ConcreteHandler1(Handler):

    start, end = 10, 20

    def check_range(self, request):
        if self.start <= request < self.end:
            print('request {} handled in handler 1'.format(request))
            return True


class ConcreteHandler2(Handler):

    def check_range(self, request):
        start, end = self.get_interval()
        if start <= request < end:
            print('request {} handled in handler 2'.format(request))
            return True

    @staticmethod
    def get_interval():
        return (20, 30)


class FallBackHandler(Handler):

    @staticmethod
    def check_range(request):
        print('end of chain, no handler for {}'.format(request))
        return False

这里要注意一下FallBackHandler这个类,这个类表示责任链的结尾,也就是它是最后一个节点。如果请求一直穿过前面的节点送到了它这里,就表示前面没有相应的handler,这个请求无法响应。

最后我们来看下实际运行的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if __name__ == '__main__':
    h0 = ConcreteHandler()
    h1 = ConcreteHandler1()
    h2 = ConcreteHandler2(FallBackHandler())
    h0.successor = h1
    h1.successor = h2

    requests = [2, 5, 14, 22, 18, 3, 35, 27, 20]
    for x in requests:
        h0.handle(x)

我们可以看到我们创建了一系列handler的实例,然后将它们串联了起来。如果我们运行这段代码的话会发现我们不同的请求被不同的handler响应了。

到这里关于责任链这个设计模式的介绍就结束了,这个设计模式非常巧妙,将上游和下游分裂开来,彼此之间消除了耦合。但是这也并不是完美的,它也有自己的缺点,一个很明显的缺点就是这样的效率会比较低。因为一层层链路传递都是需要时间的,尤其是这些链路如果还要远程调用的话,那么每一层转换都会有延迟以及相应时间,所以整个系统的效率可能会不高。

还有呢就是我们调试的时候可能会不太方便,错误不太容易排查。除此之外还有一些其他的优缺点,大家可以自己思考一下,在下方留言。

衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、在看、转发

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coder梁 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
责任链模式
概述 概念:责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道
xiangzhihong
2018/02/05
6620
责任链模式
Python抽象类介绍
与java一样,python也有抽象类的概念。抽象类是一种特殊的类,它只能有抽象方法,不能被实例化,在子类继承抽象类时,不能通过实例化使用其抽象方法,必须实现该方法。
鳄鱼儿
2024/05/21
1810
责任链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将接收对象连城一条链,并沿着链传递该请求,直到有一个对象处理它为止。 ——《设计模式:可复用面向对象软件的基础》
mingmingcome
2023/03/20
3230
责任链模式
设计模式-责任链模式
看下具体的代码: 先把抽象处理者写出来,他是一个抽象类或者接口,这里使用抽象类,每个处理者给一个名字 name 属性
breezedancer
2018/09/12
5060
设计模式-责任链模式
PHP设计模式之责任链模式
GoF定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
硬核项目经理
2019/09/18
5570
PHP设计模式之责任链模式
重温设计模式 --- 责任链模式
责任链模式是一种行为型设计模式,它通过一条由多个处理器组成的链来处理请求,每个处理器都有机会处理请求,如果一个处理器不能处理该请求,它会将请求传递给下一个处理器,直到请求被处理为止。
Niuery Diary
2023/10/22
1800
重温设计模式 --- 责任链模式
设计模式10之责任链模式
我们有这样一个场景,假如你出差需要报销。你的报销单审批人依次是:项目经理、科室主任、部门负责人、财务负责人。当然你不需要知道每个审批人的姓名、电话号码、办公地址等信息。你不需要自己一个一个的去找这些审批人审批。
Lvshen
2022/05/05
3200
设计模式10之责任链模式
责任链模式浅析
某天你想请个假,比如1到3天,直接主管可以审批;3天以上需要部门主管审批;15天以上需要副总裁审批 ... ...
孟君
2019/09/17
4320
责任链模式浅析
设计模式之责任链模式
责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。如Spring Secuurity的处理。
关忆北.
2021/12/07
3960
设计模式之责任链模式
Python 抽象基类 ABC :从实践到优雅
今天我们来聊聊 Python 中的抽象基类(Abstract Base Class,简称 ABC)。虽然这个概念在 Python 中已经存在很久了,但在日常开发中,很多人可能用得并不多,或者用得不够优雅。
Piper破壳
2024/12/25
2260
责任链模式(Chain of Responsibility)
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. (为了避免请求者与响应者之间的耦合性,让每个对象都有处理请求的机会。把这些处理请求的对象连成一条链,并沿着这条链传递请求,直到有对象能够处理这个请求为止)
刘开心_1266679
2019/02/14
5480
责任链模式(Chain of Responsibility)
设计模式(三) 责任链模式
责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
用户1518699
2019/07/19
6600
设计模式(三) 责任链模式
设计模式----责任链模式
一个事件需要经过多个对象处理是一个挺常见的场景,譬如采购审批流程,请假流程,软件开发中的异常处理流程,web请求处理流程等各种各样的流程,可以考虑使用责任链模式来实现。
大忽悠爱学习
2021/11/15
6680
设计模式之责任链模式
本文通过图书馆管理系统中,用户名校验、密码校验、需要增加问题,每次都要增加if判断语句,将其改用责任链模式进行链式调用,为了让代码更加的优雅,我们使用之前学过的建造者模式就代码进行改造。接着我们会介绍责任链模式在我们常用的框架中的运用,最后是责任链模式的优缺点和应用场景。
程序员田同学
2022/08/24
3630
设计模式之责任链模式
Java设计模式(十七)----责任链模式
责任链模式 一、 概念 二、 引子 三、 结构 四、 具体案例 一、概念 责任链模式是一种对象的行为模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到 链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织 和分配责任。 二、引子 从击鼓传花谈起   击鼓传花是一种热闹而又紧张的饮酒游戏。在酒宴上宾客依次坐
汤高
2018/01/11
1.7K0
Java设计模式(十七)----责任链模式
Java描述设计模式(15):责任链模式
这里分析的几个方法,都是从DispatcherServlet类的doDispatch方法中请求的。
知了一笑
2019/10/23
4310
Java描述设计模式(15):责任链模式
聊一聊责任链模式
责任链模式(Chain of Responsibility Pattern)是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止,属于行为型模式。下面放一张足球比赛的图,通过层层传递,最终射门。通过这张图,可以更好的理解责任链模式。
知了一笑
2022/11/30
3710
聊一聊责任链模式
抽象类的使用和作用
抽象类是一种特殊的类,生来就是作为父类的,具有抽象方法。那我们就来看一下代码,通过代码我们来学习抽象类的定义和使用。
罗罗攀
2021/07/28
9240
抽象类的使用和作用
Java面向对象设计之责任链模式
很多情况下,在一个软件系统中可以处理某个请求的对象不止一个。例如审批工作流等,他们可以构成一条处理采购单的链式结构,采购单(可以看作是要处理的信息)沿着这条链进行传递,这条链就称为责任链。责任链可以是一条直线、一个环或者一个树形结构,最常见的职责链是直线型,即沿着一条单向的链来传递请求。链上的每一个对象都是请求处理者,责任链模式可以将请求的处理者组织成一条链,并让请求沿着链传递,由链上的处理者对请求进行相应的处理。在此过程中,客户端实际上无须关心请求的处理细节以及请求的传递,只需将请求发送到链上即可,从而实现请求发送者和请求处理者解耦。
Abalone
2022/07/14
5430
Java面向对象设计之责任链模式
调用像链子,责任链模式
在责任链模式中,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上进行传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。责任链模式的UML类图如下:
BUG弄潮儿
2021/01/06
3890
相关推荐
责任链模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档