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

