Java设计模式之责任链模式的概念、实现以及netty中的责任链模式
本文先介绍了责任链模式的概念及简单实现。再贴了netty中对责任链的实现。最后总结了一点点思考。
1、概念相关
1.1、概念
责任链模式为请求创建了一个接收者对象的链,每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,沿着这条链传递请求,直到有对象处理它为止。
1.2、解决了什么:
客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
1.3、场景:
1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3、可动态指定一组对象处理请求。
2、简单实现
2.1代码
注:代码中getter和setter都省略
定义一个请求
publicclassRequest{ privateStringname; privateintdays; }
定义一个返回结果
publicclassResult{ privatebooleanagree; publicResult(booleanagree){ this.agree=agree; } }
定义一个处理接口
publicinterfaceHandler{ //每个处理器持有链,能取到链上的请求或者传递请求 Resultdeal(Chainchain); interfaceChain{ //获取请求 Requestrequest(); //传递请求 Resultproceed(Requestrequest); } }
定义一个处理接口的链
publicclassHandlerChainimplementsHandler.Chain{ //持有链要处理的请求 privateRequestrequest; //持有链上所有的处理器 privateQueuehandlers; publicHandlerChain(Requestrequest){ this.request=request; } //添加一个处理器 publicHandlerChainaddHandler(Handlerhandler){ if(handlers==null){ handlers=newLinkedList<>(); } handlers.add(handler); returnthis; } //实现接口的方法-获取请求 publicRequestrequest(){ returnrequest; } //实现接口的方法-处理请求 publicResultproceed(Requestrequest){ //取队首的处理器开始请求。如果队首的处理器处理了,直接返回结果 //如果队首的处理器选择传递请求,又会进这个proceed方法。取新的队首处理 //为什么是传递而不是直接遍历队列,如果result=null,继续;result!=null,返回呢?因为传递的方式,可以对request再次处理。A处理器可以做简单处理,再传递给B。 Handlerhandler=handlers.poll(); returnhandler.deal(this); } }
定义三个处理者
publicclassAHandlerimplementsHandler{ publicResultdeal(Chainchain){ Requestrequest=chain.request(); //只处理小于等于1的请求,大于1的请求被传递了 if(request.getDays()>1){ //这里可以对request做部分处理,再传递 returnchain.proceed(request); } System.out.println("A处理了"); returnnewResult(true); } } publicclassBHandlerimplementsHandler{ publicResultdeal(Chainchain){ Requestrequest=chain.request(); //只处理小于等于2的请求,大于2的请求被传递了 if(request.getDays()>2){ returnchain.proceed(request); } System.out.println("B处理了"); returnnewResult(true); } } publicclassCHandlerimplementsHandler{ publicResultdeal(Chainchain){ Requestrequest=chain.request(); //只处理小于等于3的请求,大于3的请求被传递了 if(request.getDays()>3){ returnchain.proceed(request); } System.out.println("C处理了"); returnnewResult(true); } }
测试
publicclassTest{ publicstaticvoidmain(String[]args){ //new一个链,往链上添加处理器 Requestrequest1=newRequest("hhy",3); HandlerChainchains=newHandlerChain(request1).addHandler(newAHandler()).addHandler(newBHandler()).addHandler(newCHandler()); Resultresult1=chains.proceed(request1); System.out.println("结果:"+result1.isAgree()); } }
结果
传入3:
返回:
C处理了
结果:true传入2:
返回:
B处理了
结果:true传入1:
返回:
A处理了
结果:true
3.netty中的责任链模式
用过netty的同学看到下面这个代码应该很熟悉了,非常简单netty客户端,创建连接,设置编解码器,发送请求。
publicstaticvoidmain(String[]args){ EventLoopGroupgroup=newNioEventLoopGroup(); NioSocketChannelchannel=newNioSocketChannel(); Bootstrapbootstrap=newBootstrap(); bootstrap.group(group).option(ChannelOption.TCP_NODELAY,true); InetSocketAddressaddress=newInetSocketAddress(InetAddress.getByName(httpRequest.host),httpRequest.port); group.register(channel); channel.connect(address).sync(); channel.pipeline().addLast("http-decoder",newHttpResponseDecoder()); channel.pipeline().addLast("http-encoder",newHttpRequestEncoder()); channel.pipeline().addLast("http-client",newHttpHandler(HttpClient.this)); channel.writeAndFlush(httpRequest); }
通过我们上面的例子,不难猜测到channel.pipeline()应该就是一个链,持有了channel上所有的处理器。pipeline()方法返回一个ChannelPipeline接口,我们直接看它的实现类
publicclassDefaultChannelPipelineimplementsChannelPipeline{ finalAbstractChannelHandlerContexthead; finalAbstractChannelHandlerContexttail; @Override publicfinalChannelPipelineaddLast(EventExecutorGroupgroup,Stringname,ChannelHandlerhandler){ finalAbstractChannelHandlerContextnewCtx; synchronized(this){ checkMultiplicity(handler); //把handler封装成AbstractChannelHandlerContext newCtx=newContext(group,filterName(name,handler),handler); //调用了addLast0方法 addLast0(newCtx); ... } callHandlerAdded0(newCtx); returnthis; } privatevoidaddLast0(AbstractChannelHandlerContextnewCtx){ //把新加入的处理器设置成尾部的前驱,原尾部的前驱设置成新处理器的后继 AbstractChannelHandlerContextprev=tail.prev; newCtx.prev=prev; newCtx.next=tail; prev.next=newCtx; tail.prev=newCtx; } }
通过addLast0,我们看到DefaultChannelPipeline使用了链表的形式存储了处理器。
继续看这个类的其他方法
publicclassDefaultChannelPipelineimplementsChannelPipeline{ ... @Override publicfinalChannelchannel(){ returnchannel; } @Override publicfinalChannelPipelinefireChannelActive(){ AbstractChannelHandlerContext.invokeChannelActive(head); returnthis; } ... }
返回当前的channel,处理channel上的事件。(就类似于我们上面的链里面有getRequest(),proceed()方法)
继续跟踪invokeChannelActive方法
abstractclassAbstractChannelHandlerContextimplementsChannelHandlerContext,ResourceLeakHint{ staticvoidinvokeChannelActive(finalAbstractChannelHandlerContextnext){ EventExecutorexecutor=next.executor(); if(executor.inEventLoop()){ next.invokeChannelActive(); }else{ executor.execute(newRunnable(){ @Override publicvoidrun(){ next.invokeChannelActive(); } }); } } //调用了invokeChannelActive privatevoidinvokeChannelActive(){ //判断是不是要处理 if(invokeHandler()){ try{ //处理 ((ChannelInboundHandler)handler()).channelActive(this); }catch(Throwablet){ invokeExceptionCaught(t); } }else{ //事件传递 fireChannelActive(); } } @Override publicChannelHandlerContextfireChannelActive(){ //执行前需要先找到一个合适的处理器invokeChannelActive invokeChannelActive(findContextInbound(MASK_CHANNEL_ACTIVE)); returnthis; } privateAbstractChannelHandlerContextfindContextInbound(intmask){ AbstractChannelHandlerContextctx=this; EventExecutorcurrentExecutor=executor(); do{ //循环,找到一个合适的处理器并返回 ctx=ctx.next; }while(skipContext(ctx,currentExecutor,mask,MASK_ONLY_INBOUND)); returnctx; } }
其实看到这,这个链已经非常的明显了。pipline持有处理器,AbstractChannelHandlerContext做了一些封装,使得链上的处理器能对事件进行传递和处理。
最后再看下handler的实现类
publicclassChannelInboundHandlerAdapterextendsChannelHandlerAdapterimplementsChannelInboundHandler{ ...... @Override publicvoidchannelActive(ChannelHandlerContextctx)throwsException{ ctx.fireChannelActive(); } ...... }
以这个ChannelInboundHandlerAdapter为例,在channel收到激活事件通知的时候,它调用了ctx.fireChannelActive();方法传递了事件。ctx是ChannelHandlerContext类型,很熟悉,它是个接口,我们上面看到的AbstractChannelHandlerContext是它的实现类。就这样,一个激活事件就在链上传递了起来。而链上的处理器就是我们最初始的测试方法里面addLast进去的。
4、思考
在netty中,事件在责任链中有序传播,事件处理器可以处理自己关心的功能,可以拦截,也可以继续传播(向前或向后)事件。上层的业务只需要关心自己的逻辑。整个架构层次分明。
OA系统的工作流似乎也特别适合责任链模式,正如我们一开始的例子,不同人审批不同的时长的假期。
对于代码里某些冗长的ifelse,是不是也有改造成责任链的可能?
if(通过第一关){
进入第二关
if(通过第二关){
进入第三关
...
}
}
改成通过第一关后传递事件,反之结束。这期间还可以灵活的做跳跃,得到某个奖励,直接跳到第三关。如果要添加第四关,也比ifelse灵活的多。
当然并不是ifelse都适合改造成责任链模式,还是要具体的业务及效率上综合考虑。个人觉得在流程上有顺序依赖的,非常适合。
到此这篇关于Java设计模式之责任链模式的概念、实现以及netty中的责任链模式的文章就介绍到这了,更多相关责任链模式的概念实现及netty的责任链模式内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
[1]https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html
[2]www.nhooo.com/article/202504.htm