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

冰花专访:什么是责任链模型?

时间:2023-03-15 18:44:50 科技观察

前言每个人都一定有过面试的经历,但是面试的过程其实很像一个设计模式。每轮面试官都有自己的职责。求职者的面试过程就像一个客户的请求。过程。在设计模式系列的文章中,创作型设计模式已经分享给大家,感兴趣的朋友可以前往之前的分享。接下来,我将分享三大类设计模式中的行为模式。今天要分享的是责任链模式定义的大纲。什么是责任链?它的原理是什么?解耦请求的发送和接收,让多个接收对象有机会处理请求。将这些接收对象串成一个链,并沿着链传递请求,直到链中的一个接收对象能够处理它。以上定义来自《设计模式之美》看一张官方图Client(客户端):实例化一个处理器链,在第一个链对象中调用handleRequest方法。Handle(processor):一个抽象类,提供给实际的processor来继承然后实现handleRequest方法来处理请求不同的ConcreteHandlers。这样看结构,其实还是比较简单的,但是我们还是用面试流程来模拟下责任链吧!代码实现假设你去一家公司面试,第一次去第一面,第二次去第二面,第三次直接pass。这个模拟面试代码怎么写?publicabstractclassHandler{protectedHandlerhandler;publicvoidsetHandler(Handlerhandler){this.handler=handler;}publicabstractvoidhandleRequest(Integertimes);}首先我们还是定义一个抽象的Handler处理器,并添加一个抽象的处理方法handleRequest,后面只需要写一个具体的processor继承Handler类publicclassFirstInterviewextendsHandler{@OverridepublicvoidhandleRequest(Integertimes){//条件判断是否属于当前Handler的处理范围,如果不属于则将Handler处理器向下传递if(times==1){//这里假设是处理System.out.println("firstinterview"+times);}handler.handleRequest(times);}}的业务逻辑代码其次,构建firstinterviewHandler,内部实现handleRequest方法。判断是否是当前处理应该处理的业务逻辑,如果不是,则向下传递。同样secondSecondInterview和FirstInterview代码基本一样,就不给大家贴出来了,直接看最后的publicclassThreeInterviewextendsHandler{@OverridepublicvoidhandleRequest(Integertimes){if(times==3){System.out.println("第三次面试"+times+",恭喜面试通过,HR会联系你!!!");}}publicstaticvoidmain(String[]args){Handlerfirst=newFirstInterview();Handlersecond=newSecondInterview();Handlerthree=newThreeInterview();first.setHandler(second);second.setHandler(three);//第一次面试first.handleRequest(1);System.out.println();//第二次面试first.handleRequest(2);System.out.println();//第三次面试first.handleRequest(3);System.out.println();}}这个结果可以很明显的看到,根据我们的参数,不同的Handler根据自己的职责处理自己的自己的事业,这是责任链。框架的应用责任链也体现在很多框架的源代码中。比如开始学习SpringMVC中的ServletFilter和Spring中的SpringInterceptor其实就是利用了责任链模型的思想来实现框架的可扩展性的同时也遵循了开闭原则。DUBBO作为一个通用的RPC框架,也有这种责任链的思想。大家思考的问题?dubbo服务一旦暴露出来,基本上任何服务都可以调用,但是在一些特殊的业务中,我们需要暴露服务,但又不想被不懂业务的人随便调用。比如:修改商品库存的dubbo服务,我们只允许调用一些特定的场景,比如下单、购物车、添加修改商品等。那么有没有办法在Provider端进行拦截,只允许调用特定的服务,否则拦截不允许执行呢?第一种方法是添加服务名APP_NAME作为参数校验,这种方法很常见,也是最容易想到的方法。第二种方法是实现一个DUBBO拦截器来选择性过滤RPC调用。对于以上两种方法,下面我来详细说说第二种方法的实现方法。每个公司都会在已有的DUBBO源码的基础上做自己的具体改动,所以第二种方法也需要我们改行有dubbo源码。首先修改ConsumerContextFilter消费者拦截器这里我们以dubbo2.7.19版本为例。在ConsumerContextFilter中为Attachments添加APP_NAME,那么这个RPC调用就可以拿到我们从Attachments中插入的值。至于这个APP_NAME的获取,可以通过System.getProperty("project.name","")获取服务名称,这里我就不在DUBBO上过多展开了,有强烈建议的可以解释一下。然后,在完成设计模式之后,我会详细分析dubbo,zookeeper中的ZAB,共识选举算法等。由于CONSUMER已经填写了服务名,所以只需要在Provider中写一个ProviderFilter即可。这里基本上就是如何处理每一个RPC调用的拦截,然后你想在provider中的filter中拦截service。指定这个DubboProviderFilter即可,也可以全局实现。注意:如果在DUBBO包中使用了这个Filter,千万不要弄错了。既然在实际业务改造的示例框架中存在这种思路,那么如何应用到业务代码中呢?举个例子:我们可以在不同的模块中展示商品详情,比如header图片、商品信息、sku信息、收货地址、分期付款等。那么如何组装成商品详情的展示呢?publicabstractclassAbstractDataHandler{//处理模块化数据protectedabstractTdoRequest(Stringquery)throwsException;}首先我们还是定义一个抽象数据Handler,然后创建ItemInfoHandler和SkuInfoHandler来继承抽象handler@ComponentpublicclassItemInfoHandlerextendsAbstractDataHandler{@OverrideprotectedItemInfodoquestRequest.Item(Stringquery){ItemInfoHandler.ItemInfoinfo=newItemInfo();info.setItemId(123456L);info.setItemName("测试商品");returninfo;}@DatapublicstaticclassItemInfo{privateLongitemId;privateStringitemName;}}同样SkuInfoHandler类也是一样的@ComponentpublicclassSkuInfoHandlerextendsAbstractDataHandler{@OverrideprotectedSkuInfoHandler.SkuInfodoRequest(Stringquery){SkuInfoHandler.SkuInfoinfo=newSkuInfoHandler.SkuInfo();info.setSkuId(78910L);info.setSkuName("测试SKU");returninfo;}@DatapublicstaticclassSkuInfo{privateLongskuId;privateStringskuName;}}最后是我们的测试代码@ComponentpublicclassDataAggregation{@AutowiredprivateSkuInfoHandlerskuInfoHandler;@AutowiredprivateItemInfoHandleritemInfoHandler;publicMapconvertItemDetail()throwsException{Mapresult=newHashMap();result.put("skuInfoHandler",skuInfoHandler.doRequest("模拟数据请求"));result.handlerInfohandler("iteminfoHandler"),("模拟数据请求"));returnresult;}publicstaticvoidmain(String[]args)throwsException{ApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");DataAggregationdataAggregation=(DataAggregation)applicationContext.getBean("dataAggregation");Mapmap=dataAggregation.convertItemDetail();System.out.println(JSON.toJSONString(map));//打印结果数据//{"skuInfoHandler":{"skuId":78910,"skuName":"测试SKU"},"itemInfoHandler":{"itemId":123456,"itemName":"TestProduct"}}}}这个例子其实稍微修改了一下,我们没有把handler传下去,而是通过conver中的实际业务逻辑tItemDetail方法用于构造各个模块的数据,最后返回一个Map结构数据。这里其实还有另一种写法。每个需要处理的Handler可以加载到一个List容器中,然后循环调用每个Handler。当然,这个里面的doRequest方法是为其他一些业务场景写的。看完你还可以发现,每个Handler都是可以共享的,每个业务的代码逻辑都非常清晰。写这样的代码感觉很舒服。综上所述,设计模式不是一成不变的,适合自己当前业务的模式才是最好的模式。理解前人的思路,结合我们需要的模式。本次分享到此结束,接下来给大家分享行为设计模式。