設計模式學習總結:責任鏈模式
本文為筆者在閱讀一些書籍、部落格、專欄等資料後所總結的個人對於責任鏈模式的筆記,由於筆者才疏學淺,若有不足之處,還望各位加以斧正,您的建議與鼓勵都是筆者源源不斷的前進動力。感謝!
文章大綱如下:
- 責任鏈模式的簡單認識
- 責任鏈模式的簡單應用
- 兩種責任鏈的實現方法:基於陣列、連結串列
- 責任鏈的一些簡單應用
- 責任鏈模式在原始碼中的體現
- 責任鏈模式的優缺點
責任鏈模式簡單認識
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ---- 《設計模式》GoF
中文意思為:
通過給多個物件處理請求的機會,避免請求的傳送方與其接收方耦合。將接收物件串起來,並沿著鏈傳遞請求,直到有一個物件處理它。
筆者的將其理解為:
現在有一條責任鏈(其中包含著各種接收物件,也可以理解為處理器,其具有處理請求的方法)。傳送方對責任鏈傳送請求,責任鏈中的物件A接收到請求後,若其能處理該請求,則處理,否則就向下一個物件傳遞請求,直到有一個物件能對其進行處理。整個過程形成一條鏈。
此外,責任鏈模式還有另一種實現方式,即
請求進入責任鏈,物件A接收到請求後,若需要處理,則處理,處理後傳遞給下一個物件;若不需要處理,則直接傳遞給下一個物件。也就是說,其最終都會將該物件傳遞給鏈上的下一個物件,以此類推,直到最後一個物件處理後,方結束。此變體中的請求會被責任鏈中的每個處理器(物件)處理。
責任鏈模式的簡單應用
這兩種責任鏈的實現方法
以下實現程式碼參考自王爭先生在極客時間上的《設計模式之美》所展示的程式碼片段。我在其中註釋部分加入了自己的理解、總結。
第一種責任鏈(即處理後就返回)
此責任鏈的實現方法有兩種,分別是陣列實現和連結串列實現。
- 陣列實現
陣列實現的大致模板:
/** * 處理者介面,定義了處理者的行為 */ interface IHandler { boolean handle(); } /** * 處理者A */ class HandlerA implements IHandler { @Override public boolean handle() { boolean status = true; // 此處省略處理任務的方法…… // 如果任務被處理了,就令status為true;否則令其為false System.out.println("[HandlerA]: status is " + status); return status; } } /** * 處理者B */ class HandlerB implements IHandler { @Override public boolean handle() { boolean status = true; // 此處省略處理任務的方法…… // 如果任務被處理了,就令status為true;否則令其為false System.out.println("[HandlerB]: status is " + status); return status; } } /** * 責任鏈 */ class HandlerChain { // 責任鏈的陣列儲存物件 private List<IHandler> handlerList = new ArrayList<>(); // 向陣列中新增handler的方法,新增成功返回true,反之為false public boolean addHandler(IHandler handler) { return this.handlerList.add(handler); } // 責任鏈呼叫處理者去處理請求 public void handle() { // 遍歷責任鏈上的處理者 for (IHandler handler : handlerList) { boolean status = handler.handle(); // 如果處理者返回的是true,就說明已經處理請求了,可以退出遍歷了 // 反之,繼續遍歷,直到被處理 if (status == true) { break; } } } } /** * 基於陣列實現的責任鏈(處理後就停止的責任鏈) */ public class Demo { public static void main(String[] args) { HandlerChain handlerChain = new HandlerChain(); handlerChain.addHandler(new HandlerA()); handlerChain.addHandler(new HandlerB()); handlerChain.handle(); } }
輸出結果為:
可見,HandleA處理了請求後,就不再往後傳遞請求了。
陣列實現的場景應用:
/**
* 先留個坑...有時間了來填...
*/
- 連結串列實現
連結串列實現的大致模板:
/**
* 處理者抽象類,定義了處理者的行為
*/
abstract class IHandler {
// 下一個處理者
protected IHandler nextHandler;
// 子類需要實現的處理方法
protected abstract boolean doHandle();
// 設定下一個處理者
public void setNextHandler(IHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 獲取下一個處理者
public IHandler getNextHandler() {
return this.nextHandler;
}
// 父類呼叫子類的處理方法判斷是否向後傳遞請求
public final void handle() {
boolean status = doHandle();
if ((!status) && (this.nextHandler != null)) {
this.nextHandler.handle();
}
}
}
/**
* 處理者A
*/
class HandlerA extends IHandler {
@Override
public boolean doHandle() {
boolean status = false;
// 此處省略處理任務的方法……
// 如果任務被處理了,就令status為true;否則令其為false
System.out.println("[HandlerA]: status is " + status);
return status;
}
}
/**
* 處理者B
*/
class HandlerB extends IHandler {
@Override
public boolean doHandle() {
boolean status = true;
// 此處省略處理任務的方法……
// 如果任務被處理了,就令status為true;否則令其為false
System.out.println("[HandlerB]: status is " + status);
return status;
}
}
/**
* 責任鏈
*/
class HandlerChain {
// 責任鏈的頭結點
private IHandler headHandler;
// 責任鏈的尾結點
private IHandler tailHandler;
// 向責任鏈中新增處理者
public void addHandler(IHandler handler) {
// 把處理者的下一個結點設為空
handler.setNextHandler(null);
// 如果頭結點為空,說明責任鏈現在沒有任何處理者,此時把頭結點和尾結點都設定為函式傳入的IHandler例項
if (this.headHandler == null) {
this.headHandler = handler;
this.tailHandler = handler;
return; // 設定好後就返回,因為後面要給head非空時的情況賦值
}
// headHandler非空時,把傳進來的結點加入到連結串列尾部
this.tailHandler.setNextHandler(handler);
this.tailHandler = handler;
}
// 責任鏈呼叫處理者去處理請求
public void handle() {
if (this.headHandler != null) {
this.headHandler.handle();
}
}
}
/**
* 基於連結串列實現的責任鏈(處理後就停止的責任鏈)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
handlerChain.addHandler(new HandlerA());
handlerChain.addHandler(new HandlerB());
handlerChain.handle();
}
}
輸出結果:
連結串列實現的場景應用:
/**
* 先留個坑...有時間了來填...
*/
第二種責任鏈(一直往後傳遞)
此責任鏈的實現方法也是兩種,即陣列、連結串列實現。
- 陣列實現
陣列實現的大致模板:
/**
* 處理者介面,定義了處理者的行為
*/
interface IHandler {
// 實現類的處理方法
void handle();
}
/**
* 處理者A
*/
class HandlerA implements IHandler {
@Override
public void handle() {
// 此處省略處理任務的方法…
System.out.println("[HandlerA]: handle()");
}
}
/**
* 處理者B
*/
class HandlerB implements IHandler {
@Override
public void handle() {
// 此處省略處理任務的方法……
System.out.println("[HandlerB]: handle()");
}
}
/**
* 責任鏈
*/
class HandlerChain {
// 責任鏈的儲存物件
List<IHandler> handlerList = new ArrayList<>();
// 向責任鏈中新增處理者
public HandlerChain addHandler(IHandler handler) {
handlerList.add(handler);
return this;
}
// 責任鏈呼叫處理者去處理請求
public void handle() {
for (IHandler handler : handlerList) {
handler.handle();
}
}
}
/**
* 基於陣列實現的責任鏈(處理後就停止的責任鏈)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
IHandler handlerA = new HandlerA();
IHandler handlerB = new HandlerB();
handlerChain.addHandler(handlerA).addHandler(handlerB);
handlerChain.handle();
}
}
輸出結果:
陣列實現的場景應用:
/**
* 先留個坑...有時間了來填...
*/
- 連結串列實現
連結串列實現的大致模板:
/**
* 處理者抽象類,定義了處理者的行為
*/
abstract class IHandler {
// 下一個處理者
protected IHandler nextHandler;
// 子類需要實現的處理方法
protected abstract void doHandle();
// 設定下一個處理者
public void setNextHandler(IHandler nextHandler) {
this.nextHandler = nextHandler;
}
// 獲取下一個處理者
public IHandler getNextHandler() {
return this.nextHandler;
}
// 父類呼叫子類的處理方法並向後請求
public final void handle() {
doHandle();
// 與前一種責任鏈所不同的是,這裡不需要通過前面是否處理判斷是否傳遞,而是一直傳遞
if (this.nextHandler != null) {
System.out.println("[IHandler]-nextHandler: " + this.nextHandler.getClass());
this.nextHandler.handle();
}
}
}
/**
* 處理者A
*/
class HandlerA extends IHandler {
@Override
public void doHandle() {
// 此處省略處理任務的方法…
System.out.println("[HandlerA]: doHandler()");
}
}
/**
* 處理者B
*/
class HandlerB extends IHandler {
@Override
public void doHandle() {
// 此處省略處理任務的方法……
System.out.println("[HandlerB]: doHandler()");
}
}
/**
* 責任鏈
*/
class HandlerChain {
// 責任鏈的頭結點
private IHandler headHandler;
// 責任鏈的尾結點
private IHandler tailHandler;
// 向責任鏈中新增處理者
public void addHandler(IHandler handler) {
// 把處理者的下一個結點設為空
handler.setNextHandler(null);
// 如果頭結點為空,說明責任鏈現在沒有任何處理者,此時把頭結點和尾結點都設定為函式傳入的IHandler例項
if (this.headHandler == null) {
this.headHandler = handler;
this.tailHandler = handler;
return; // 設定好後就返回,因為後面要給head非空時的情況賦值
}
// headHandler非空時,把傳進來的結點加入到連結串列尾部
this.tailHandler.setNextHandler(handler);
this.tailHandler = handler;
}
// 責任鏈呼叫處理者去處理請求
public void handle() {
if (this.headHandler != null) {
this.headHandler.handle();
}
}
}
/**
* 基於連結串列實現的責任鏈(處理後就停止的責任鏈)
*/
public class Demo {
public static void main(String[] args) {
HandlerChain handlerChain = new HandlerChain();
handlerChain.addHandler(new HandlerA());
handlerChain.addHandler(new HandlerB());
handlerChain.handle();
}
}
輸出結果:
可以看到,HandlerA在處理了請求後,又傳遞給了HandlerB,HandlerB也對請求進行了處理。
連結串列實現的場景應用:
/**
* 先留個坑...有時間了來填...
*/
責任鏈模式在原始碼中的體現
/**
* 先留個坑...有時間了來填...
*/
責任鏈模式的優缺點
最後,總結下責任鏈模式的優缺點。
優點
- 將請求的傳送者和接收者解耦。
- 簡化物件,減少程式碼的複雜性。(處理者不需要知道責任鏈的內部構造以及如何處理的)
- 滿足開閉原則,提高程式碼的擴充套件性。(動態地新增或刪除責任)
缺點
- 不容易觀察執行時的特徵,出現問題不好排查。(如果某一節點出現問題,則容易造成系統崩潰)
- 並不一定保證請求一定會執行。(責任鏈過長時,請求沒被處理或者處理時間過長,會影響整體效能)