前言
寫(xiě)方法的原則要盡量做到一個(gè)方法只做一件事情济舆, 這樣做到職責(zé)單一抵怎, 日后對(duì)于代碼的維護(hù)也非常容易浓恶。 并且代碼的復(fù)用性也會(huì)很高玫坛。
在某些情況下, 對(duì)于某個(gè)事件或某個(gè)消息的處理是一個(gè)接一個(gè)的包晰, 而又不想以下這種寫(xiě)法
process(Message a){
b = doSomethingA(a)
c = doSomethingB(b)
d = doSomethingC(c)
e = doSomethingD(d)
}
以上這種寫(xiě)法很容易湿镀, 可以明顯看到執(zhí)行的順序,也可以保證一個(gè)方法只做一件事情伐憾, 讓后將這些事情連起來(lái)勉痴。
一些弊端
代碼不易維護(hù)
假如在第二步中我們想改為多線程執(zhí)行, 則需要改動(dòng)process中的代碼树肃, 而針對(duì)職責(zé)鏈則僅需加入多線程節(jié)點(diǎn)來(lái)使得后續(xù)的處理變?yōu)槎嗑€程蒸矛。調(diào)用關(guān)系的臃腫
我們能看到, 上面代碼的調(diào)用是process作為調(diào)用方胸嘴, 連續(xù)調(diào)用a,b,c,d方法雏掠, process未必僅僅是調(diào)用a,b,c,d 。在程序種往往要進(jìn)行邏輯判斷劣像, 比如如果B方法的的返回值c為null磁玉, 則不再向后執(zhí)行,那么我們可能會(huì)在process方法種在后面加上該判斷
if(c == null){
return;
}
如果是更復(fù)雜的邏輯處理驾讲, 異常處理等蚊伞, 就需要在process方法種加入相應(yīng)的邏輯處理代碼, 從而使得代碼變得越來(lái)越臃腫吮铭。
換一種思路
這時(shí)候我們想时迫, 能否使得上一次的調(diào)用對(duì)下一次的調(diào)用負(fù)責(zé), 而不是通過(guò)process這層來(lái)管理谓晌, 如下圖顯示
那么像這樣的如鏈?zhǔn)秸{(diào)用的機(jī)構(gòu)掠拳, 就叫做職責(zé)鏈。
其實(shí)在java中纸肉,已經(jīng)有很多地方遇到過(guò)它溺欧。
我們?cè)谀睦镆?jiàn)過(guò)職責(zé)鏈這種模式
- java web filter
在java web開(kāi)發(fā)中, 最早的應(yīng)該就屬filter了
實(shí)現(xiàn)Filter接口柏肪, 重寫(xiě)doFilter.
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chian) throws IOException, ServletException {
chian.doFilter(...)
}
如果接觸過(guò)socket編程姐刁, 對(duì)mina 或者netty框架熟悉的話, 責(zé)任鏈模式將更不陌生烦味。
在mina中:
通過(guò)FilterChianBuilder, FilterChain, IoFilter 構(gòu)成一個(gè)鏈聂使。 創(chuàng)建方式如下
DefaultIoFilterChainBuilder builder = new DefaultIoFilterChainBuilder();
builder.addFirst("mdcInjectionFilter", new MdcInjectionFilter(MdcInjectionFilter.MdcKey.remoteAddress));
builder.addAfter("mdcInjectionFilter", "codExecutorFilter", new ExecutorFilter());
builder.addAfter("codExecutorFilter", "loggingFilter", new LoggingFilter());
builder.addAfter("loggingFilter", "codecFilter", codecFilter);
builder.addAfter("codecFilter", "executorFilter", executorFilter);
builder.addAfter("executorFilter", "writeExecutorFilter", new ExecutorFilter(IoEventType.WRITE));
DefaultIoFilterChain chain = new DefaultIoFilterChain();
builder.buildFilterChain(chain);
這樣就構(gòu)造了一條職責(zé)鏈, 每一個(gè)節(jié)點(diǎn)負(fù)責(zé)下面一個(gè)節(jié)點(diǎn), 節(jié)點(diǎn)內(nèi)執(zhí)行一定的邏輯柏靶。 至于如何構(gòu)造職責(zé)鏈可以看我的mina源碼分析弃理。
- netty 中
在netty中, 核心的業(yè)務(wù)邏輯有一系列ChannelHandler處理屎蜓。
netty中分為 inBound 和 outBound, 在channelpipeline 中痘昌, inbound 和outbound channelhandler 處理讀和寫(xiě)。
可以通過(guò)下面代碼創(chuàng)建:
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("handler1", firstHandler);
pipeline.addFirst("handler2", new SecondHandler());
pipeline.addLast("handler3", new ThirdHandler());
當(dāng)然在netty還有其他幾個(gè)重要成員Channel, ChannelHandlerContext, EventLoop, EventLoopGroup, 這些不在這里闡述炬转, 有興趣的話可以看《netty in action》控汉。
后話
職責(zé)鏈模式能讓模塊結(jié)構(gòu)更加清晰, 節(jié)點(diǎn)與節(jié)點(diǎn)之間的修改影響會(huì)非常小返吻。 但是并不適合所有的場(chǎng)景姑子, 要結(jié)合具體的場(chǎng)景分析。