当前位置: 首页 > 科技观察

本文将带你领略“责任链”设计模式

时间:2023-03-19 09:56:53 科技观察

前言已经工作过的,应该看过“责任链”的面向对象设计模式,还在上学的小伙伴们不需要它。别担心,迟早你会接触到它。本文旨在让对责任链还不熟悉的小白同学和朋友能够快速对这种设计模式有一个大概的了解。在我们的工农业生产中,经常会出现这样的场景:一个任务、交易、流程等,需要通过很多不同的步骤来完成不同的计算或者采集不同的数据。为了维护一个复杂的、有时甚至是顺序敏感的任务流,我们在代码编写和设计中经常使用“责任链”设计模式。什么是“责任链”?让我们看看下面的例子。例子假设你也“穿越”到了清朝,会写代码的和珅、中堂,皇帝即将南巡。请用代码封装模拟“乾隆下江南”的事件。万岁爷的行程怎么安排?要知道这是个大工程,过程中不能有任何差错。万一出事,你会失去理智吗?但皇上是个脾气暴躁的人,行程可能经常变动,甚至中途就私下微博看看。所以,我们在等待皇帝出入江南时,既要让皇帝的行程有条不紊地进行,又要尽量适??应皇帝可能出现的变化。一时兴起。如何设计?如果一起写执行皇帝的行程,有两个弊端:行程太多,而且个个都很重要,这么远的路还得自己一个人操办。如果出现问题,头部将不得不移动;时间表很多,添加和更改太麻烦。一旦有异动,圣者的行程就很容易变得混乱。毕竟行程是写在一起的,像一堆乱七八糟的东西,看不清楚。那么问题来了,大人您是怎么安排皇上的行程的?别着急,看看地图就知道,乾隆从北京到杭州(基本上就是现在的),要经过直隶、山东、江苏、浙江四省。铁路):这样,何氏夫妇就可以大致将任务按照省份划分为四个部分,指示四省的干部分担这个大工程,把各自应尽的责任串成一个有序的链条。然后让他们轮流执行侍奉皇帝的任务。这样就解决了行程过于丰富,无法单独安排大人的问题。二是保证了每个步骤的灵活安排(下例)。否则都是你的错)。好了说了这么多,现在切入技术层面。设计Step1:首先总结我们正在研究的问题中的名词,确定大概需要哪些类别:皇帝(乾隆)行程总管(何中堂)和省级官员(工作的公务员)Step2:然后确定每个类别的他们之间的关系:最容易看出省里的官员是同事。都是要接待乾隆的,只是皇帝南巡时的出场顺序和具体的接待行为有所不同。例如:直隶总督带乾隆去避暑山庄,山东巡抚带皇上参拜文庙,苏州织造让皇上游园,杭州巡抚带皇上到西湖苏堤。这里有一个OOD优化设计的小公式:改变抽象接口,相同的建模版本。所以我们在这里面对官员的不同行为时,最好将它们抽象成接口或者抽象类。这里我们使用抽象类Official。而作为首席大臣,他不仅要掌握皇帝的动向,还要控制各省的官员。因此,在班级层面,总理班级必须参考皇帝和官员名单。下面是UML图。各省同僚UML图:而你和你的皇上作为乾隆面前的名流,要统筹安排皇帝的行程,不仅要挟持皇帝,还要控制各省官员,让他们可以有条不紊地执行任务:一般至少有一个责任链将待处理的对象作为参数传递到每个步骤中,这里的乾隆就是待处理(服务)的对象。代码是一个官员的抽象类。我们考虑实际情况。他要安排一个地方,陪皇上参观游览。其实就是一句话:侍奉皇帝。所以他有一个抽象方法serve,接受的是皇帝(Emperor)的对象@DatapublicabstractclassOfficial{protectedStringtitle;protectedabstractvoidserve(Emperoremperor);@OverridepublicStringtoString(){returntitle;}}这里为了区分不同的官员,我们还给了官员(官方)A类成员变量标题。Official下有具体的实现类,代表省级官员。他们有自己特定的方式来侍奉皇帝。比如直隶总督,他是这样做的:在serve中,完全让参数“皇帝”来决定怎么玩,(顺便说一句题外话,这种参数“洋僧”的念经方式在各种设计模式中很常见。如果你改皇上这里给Comparator,相信很多朋友会觉得有点像策略模式。而且《直隶总督》也可以在皇上之前玩或者后面单独做一些事情,这个是不是很像InvocationHandler在使用的时候对待Method的方式JDK代理?还是Spring中Aspect的处理?另外,在Visitor等设计模式中也可以看到这种写法)其他官员的写法类似,只是换个地方让皇帝拜访,见稍后输出结果,此处省略。如皇帝,乾隆就是想玩。当然,你和中堂可以安排地方官员陪同,所以皇帝班只有一种玩法。在这里,一个字符串被用来简单地指示访问的地方。为了防止有人在乾隆南巡期间在北京“立新皇帝”(执行newEmperor()),这个“皇帝”对象的创建过程采用了单例模式,以保证在北京只有这样一个皇帝整个JVM,名字叫“乾隆”。":publicclassEmperor{privatestaticfinalEmperorINSTANCE=newEmperor("乾隆");privatefinalStringname;privateEmperor(Stringname){this.name=name;}publicstaticEmperorgetInstance(){returnINSTANCE;}publicvoidplay(Officialofficial,Stringplace){System.out.println(official.getTitle()+"安排"+姓名+"皇上到??访:"+地点);}}而你和珅和你的主公,只需按各省的顺序安排以下官员,然后请皇上宣告即可天下:圣人已下江南,沿途各省要小心:publicclassPrimeMinister{privatestaticListlist=newArrayList<>();publicstaticvoidmain(String[]args){//Order沿途各省官员准备list.add(newHebeiOfficial());list.add(newShandongOfficial());list.add(newJiangsuOfficial());list.add(newZhejiangOficial());//请带上outtheEmperor=Emperor.getInstance();//向世界宣告:万岁神降剑嘎嘎!沿途省份依次侍奉皇帝ror){for(Officialo:officials){o.serve(emperor);}}}看看你的任务是不是简洁了很多,只需要维护沿途各省官员名册即可。更重要的是,你不用自己负责,下面的人不做好本职工作,会要脑袋的!只要您的“花名册”或“时间表”是正确的,我们的头脑就会保持不变。而且,各个官员的任务都比较简单,他们自己也不太容易出错。以下是整个行程模拟的执行:乾隆下江南!直隶总督安排乾隆皇帝游览:山东避暑山庄安排乾隆皇帝游览:曲阜文庙苏州织造安排乾隆皇帝游览:苏州园林杭州知州安排乾隆皇帝游览A:西湖苏笛嗯,一切似乎都很好。省官们按部就班的完成了任务,好好的伺候了万岁爷。没有异常情况,我就放心了。然而,现在却出现了意想不到的情况:皇帝突然要求在途经山东时增加一个环节——大明湖三日游!你为什么特意去那里?我们不敢问!做好准备。好在我们的行程已经有了大概的轮廓,赶紧查一查大明湖到底是谁在管,哦,济南知府,就是他!现在只要将他加入“花名册”:吩咐济南太守安排皇上下榻大明湖边的三天行程,一定不能有错,否则我问你!以下是河北这边要做的改动:...以上省略...list.add(newHeibeiOfficial());//加入济南太守,让他干活,他知道怎么玩大明湖list.add(newJinanOficial());list.add(newShandongOficial());list.add(newJiangsuOficial());list.add(newZhejiangOficial());...以下简称...上另一方面,济南知府也是一个官僚系统(Official的一个子类),所以我们必须尽力让圣上陛下在大明湖玩得开心:}@Overrideprotectedvoidserve(Emperoremperor){emperor.play(this,"大明湖");}}再次执行程序,模拟皇帝的行程,输出如下:乾隆下江南!参观:避暑山庄济南知府安排乾隆皇帝游览:大明湖畔山东巡抚安排乾隆皇帝游览:曲阜文庙苏州织造安排乾隆皇帝游览:苏州园林杭州知州安排乾隆皇帝游览游览:西湖苏亭嗯,就这样吧终于又迎合了圣意,皇上以后什么行程都不怕了(只要不微服务私访,就可以去给纪晓岚微服务私访,单一职责原则,专类做专事,对吧?)。只要找到具体的地方官员,就可以给我下令:你给我最好的款待皇上,怎么款待你,你自己想办法,如果不能好好伺候我,万岁爷,我想要你的头!当然,皇帝也可能暂时删掉。对于南游的某一段,我们可以直接从行程表中删除,需要时重新添加,做到“灵活插拔”,代码改动最小。新增业务逻辑时只添加,不易出错,也保证了代码的弹性扩展,当前责任链中的步骤,如果没有状态相关信息,可以也可以组装到其他责任链中。如果是我们的真实项目,我们甚至可以在SpringBoot的配置文件中配置工作步骤列表,打开进程的类,只需要读取配置,然后依次执行各个步骤即可。这样,如果有任何修改,只需要更改配置文件,Java代码中不需要更改。总结与扩展以上其实只是责任链模型最简单的应用。它是一个有序列表,其中安装了每个任务的步骤,然后依次运行到最后。我们可以把它写在自己的程序里,或者抽象成一个产品,让别人自由扩展配置,最大限度地减少重复制造轮子的需要。像这样的工作流引擎还有很多,比如Activiti,Netflix的Conductor等等,不仅是这些,就连你最常用的SpringMVC甚至Tomcat都是使用责任链模式的,但是他们的责任链是双向的,分别处理请求和响应,而它们的处理顺序恰好相反,本质上是使用类似于递归的方法,将(Filter或Interceptor的)数组正反序遍历一次。另外,在一些持续集成和持续部署框架中,比如Jenkins,会有管道(Pipeline)的概念。当你让gitpush提交代码时,它会触发整个过程一步步开始:拉取代码(Checkoutcode)、构建(Build)、测试(Test)等,直到部署(Deploy)完成并运行脚本以关闭旧版本的服务并启动最新部署的服务。这个“管道”实际上是一个责任链,你可以用代码脚本来配置。如果不应用责任链模式,您甚至无法运行单个Java程序。因为类加载一般遵循“双亲委派”机制,它实际上是用类似递归的方法,正反序遍历一次Classloader类组成的链表。通过:),但是逻辑比较复杂,同样应用了“模板方法”的设计模式。由于本文只是对责任链模型的简单介绍,这些不会过多展开。综上所述,充分理解和应用责任链设计模式,对于我们日常工作和阅读源码都有很大的帮助。它使我们能够有效地提高代码的可扩展性和可读性。希望对您也有帮助。本文转载自微信公众号“码农田小七”,可通过以下二维码关注。转载本文请联系码农田小七公众号。