Java設計模式之責任鏈模式的概念、實現(xiàn)以及netty中的責任鏈模式
本文先介紹了責任鏈模式的概念及簡單實現(xiàn)。再貼了netty中對責任鏈的實現(xiàn)。最后總結了一點點思考。
1、概念相關
1.1、概念
責任鏈模式為請求創(chuàng)建了一個接收者對象的鏈,每個接收者都包含對另一個接收者的引用。如果一個對象不能處理該請求,那么它會把相同的請求傳給下一個接收者,沿著這條鏈傳遞請求,直到有對象處理它為止。
1.2、解決了什么:
客戶只需要將請求發(fā)送到職責鏈上即可,無須關心請求的處理細節(jié)和請求的傳遞,所以職責鏈將請求的發(fā)送者和請求的處理者解耦了。
1.3、場景:
1、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。
2、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。
3、可動態(tài)指定一組對象處理請求。
2、簡單實現(xiàn)
2.1 代碼
注:代碼中getter 和 setter都省略
定義一個請求
public class Request { private String name; private int days; }
定義一個返回結果
public class Result { private boolean agree; public Result(boolean agree) { this.agree = agree; } }
定義一個處理接口
public interface Handler { // 每個處理器持有鏈,能取到鏈上的請求或者傳遞請求 Result deal(Chain chain); interface Chain { // 獲取請求 Request request(); // 傳遞請求 Result proceed(Request request); } }
定義一個處理接口的鏈
public class HandlerChain implements Handler.Chain { // 持有鏈要處理的請求 private Request request; // 持有鏈上所有的處理器 private Queue<Handler> handlers; public HandlerChain(Request request) { this.request = request; } // 添加一個處理器 public HandlerChain addHandler(Handler handler) { if (handlers == null) { handlers = new LinkedList<>(); } handlers.add(handler); return this; } // 實現(xiàn)接口的方法- 獲取請求 public Request request() { return request; } // 實現(xiàn)接口的方法- 處理請求 public Result proceed(Request request) { // 取隊首的處理器開始請求。如果隊首的處理器處理了,直接返回結果 // 如果隊首的處理器選擇傳遞請求,又會進這個proceed方法。取新的隊首處理 // 為什么是傳遞而不是直接遍歷隊列,如果result = null,繼續(xù);result != null,返回呢? 因為傳遞的方式,可以對request再次處理。A處理器可以做簡單處理,再傳遞給B。 Handler handler = handlers.poll(); return handler.deal(this); } }
定義三個處理者
public class AHandler implements Handler{ public Result deal(Chain chain) { Request request = chain.request(); // 只處理小于等于1的請求,大于1的請求被傳遞了 if (request.getDays() > 1) { // 這里可以對request做部分處理,再傳遞 return chain.proceed(request); } System.out.println("A處理了"); return new Result(true); } } public class BHandler implements Handler { public Result deal(Chain chain) { Request request = chain.request(); // 只處理小于等于2的請求,大于2的請求被傳遞了 if (request.getDays() > 2) { return chain.proceed(request); } System.out.println("B處理了"); return new Result(true); } } public class CHandler implements Handler { public Result deal(Chain chain) { Request request = chain.request(); // 只處理小于等于3的請求,大于3的請求被傳遞了 if (request.getDays() > 3) { return chain.proceed(request); } System.out.println("C處理了"); return new Result(true); } }
測試
public class Test { public static void main(String[] args) { // new 一個鏈,往鏈上添加處理器 Request request1 = new Request("hhy", 3); HandlerChain chains = new HandlerChain(request1).addHandler(new AHandler()).addHandler(new BHandler()).addHandler(new CHandler()); Result result1 = chains.proceed(request1); System.out.println("結果:" + result1.isAgree()); } }
結果
傳入3:
返回:
C處理了
結果:true傳入2:
返回:
B處理了
結果:true傳入1:
返回:
A處理了
結果:true
3. netty中的責任鏈模式
用過netty的同學看到下面這個代碼應該很熟悉了,非常簡單netty客戶端,創(chuàng)建連接,設置編解碼器,發(fā)送請求。
public static void main(String[] args) { EventLoopGroup group = new NioEventLoopGroup(); NioSocketChannel channel = new NioSocketChannel(); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group).option(ChannelOption.TCP_NODELAY, true); InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(httpRequest.host), httpRequest.port); group.register(channel); channel.connect(address).sync(); channel.pipeline().addLast("http-decoder", new HttpResponseDecoder()); channel.pipeline().addLast("http-encoder", new HttpRequestEncoder()); channel.pipeline().addLast("http-client", new HttpHandler(HttpClient.this)); channel.writeAndFlush(httpRequest); }
通過我們上面的例子,不難猜測到 channel.pipeline() 應該就是一個鏈,持有了channel上所有的處理器。pipeline()方法返回一個ChannelPipeline接口,我們直接看它的實現(xiàn)類
public class DefaultChannelPipeline implements ChannelPipeline { final AbstractChannelHandlerContext head; final AbstractChannelHandlerContext tail; @Override public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { checkMultiplicity(handler); // 把handler封裝成AbstractChannelHandlerContext newCtx = newContext(group, filterName(name, handler), handler); // 調用了 addLast0方法 addLast0(newCtx); ... } callHandlerAdded0(newCtx); return this; } private void addLast0(AbstractChannelHandlerContext newCtx) { // 把新加入的處理器設置成尾部的前驅,原尾部的前驅設置成新處理器的后繼 AbstractChannelHandlerContext prev = tail.prev; newCtx.prev = prev; newCtx.next = tail; prev.next = newCtx; tail.prev = newCtx; } }
通過addLast0, 我們看到DefaultChannelPipeline使用了鏈表的形式存儲了處理器。
繼續(xù)看這個類的其他方法
public class DefaultChannelPipeline implements ChannelPipeline { ... @Override public final Channel channel() { return channel; } @Override public final ChannelPipeline fireChannelActive() { AbstractChannelHandlerContext.invokeChannelActive(head); return this; } ... }
返回當前的channel,處理channel上的事件。(就類似于我們上面的鏈里面有getRequest(),proceed()方法 )
繼續(xù)跟蹤invokeChannelActive方法
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint { static void invokeChannelActive(final AbstractChannelHandlerContext next) { EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeChannelActive(); } else { executor.execute(new Runnable() { @Override public void run() { next.invokeChannelActive(); } }); } } // 調用了invokeChannelActive private void invokeChannelActive() { // 判斷是不是要處理 if (invokeHandler()) { try { // 處理 ((ChannelInboundHandler) handler()).channelActive(this); } catch (Throwable t) { invokeExceptionCaught(t); } } else { // 事件傳遞 fireChannelActive(); } } @Override public ChannelHandlerContext fireChannelActive() { // 執(zhí)行前需要先找到一個合適的處理器 invokeChannelActive invokeChannelActive(findContextInbound(MASK_CHANNEL_ACTIVE)); return this; } private AbstractChannelHandlerContext findContextInbound(int mask) { AbstractChannelHandlerContext ctx = this; EventExecutor currentExecutor = executor(); do { // 循環(huán),找到一個合適的處理器并返回 ctx = ctx.next; } while (skipContext(ctx, currentExecutor, mask, MASK_ONLY_INBOUND)); return ctx; } }
其實看到這,這個鏈已經(jīng)非常的明顯了。 pipline持有處理器,AbstractChannelHandlerContext做了一些封裝,使得鏈上的處理器能對事件進行傳遞和處理。
最后再看下handler的實現(xiàn)類
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler { ...... @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.fireChannelActive(); } ...... }
以這個ChannelInboundHandlerAdapter為例,在channel收到激活事件通知的時候,它調用了ctx.fireChannelActive();方法傳遞了事件。ctx是ChannelHandlerContext類型,很熟悉,它是個接口,我們上面看到的AbstractChannelHandlerContext是它的實現(xiàn)類。就這樣,一個激活事件就在鏈上傳遞了起來。而鏈上的處理器就是我們最初始的測試方法里面addLast進去的。
4、思考
在netty中,事件在責任鏈中有序傳播,事件處理器可以處理自己關心的功能,可以攔截,也可以繼續(xù)傳播(向前或向后)事件。上層的業(yè)務只需要關心自己的邏輯。整個架構層次分明。
OA系統(tǒng)的工作流 似乎也特別適合責任鏈模式,正如我們一開始的例子,不同人審批不同的時長的假期。
對于代碼里某些冗長的if else ,是不是也有改造成責任鏈的可能?
if (通過第一關) {
進入第二關
if (通過第二關) {
進入第三關
...
}
}
改成通過第一關后傳遞事件,反之結束。這期間還可以靈活的做跳躍,得到某個獎勵,直接跳到第三關。如果要添加第四關,也比if else靈活的多。
當然并不是if else 都適合改造成責任鏈模式,還是要具體的業(yè)務及效率上綜合考慮。個人覺得在流程上有順序依賴的,非常適合。
到此這篇關于Java設計模式之責任鏈模式的概念、實現(xiàn)以及netty中的責任鏈模式的文章就介紹到這了,更多相關責任鏈模式的概念實現(xiàn)及netty的責任鏈模式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
[1] https://www.runoob.com/design-pattern/chain-of-responsibility-pattern.html
相關文章
Spring Boot2.0整合ES5實現(xiàn)文章內容搜索實戰(zhàn)
這篇文章主要介紹了Spring Boot2.0整合ES5實現(xiàn)文章內容搜索實戰(zhàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01詳解Java 序列化與反序列化(Serialization)
這篇文章主要介紹了Java 序列化與反序列化(Serialization),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習 吧2019-03-03SpringBoot中支持Https協(xié)議的實現(xiàn)
本文主要介紹了SpringBoot中支持Https協(xié)議的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01Spring多線程通過@Scheduled實現(xiàn)定時任務
這篇文章主要介紹了Spring多線程通過@Scheduled實現(xiàn)定時任務,@Scheduled?定時任務調度注解,是spring定時任務中最重要的,下文關于其具體介紹,需要的小伙伴可以參考一下2022-05-05基于Map的computeIfAbsent的使用場景和使用方式
這篇文章主要介紹了基于Map的computeIfAbsent的使用場景和使用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Spring注解驅動擴展原理BeanFactoryPostProcessor
這篇文章主要介紹了Spring注解驅動擴展原理BeanFactoryPostProcessor,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03