一、概念以及背景
責任鏈模式(Chain of Responsibility Pattern):責任鏈模式是一種創建處理對象序列(比如操作序列)的通用方案。一個處理對象可能需要在完成一些工作之後,將結果傳遞給另一個對象,這個對象接着做一些工作,再轉交給下一個處理對象,以此類推。
簡而言之,待處理的對象,需要經過好幾個步驟的處理,形成一條鏈,一個步驟處理完之後,接着往下一個步驟繼續處理。
本文涉及的代碼在github上,點擊 鏈接 可查看源碼。
本文會用兩種方式來實現責任鏈模式,第二種是用lambda表達式的方式來實現,參數行爲化的方式實現。
本文所使用的場景如下:處理一封信,首先對會信的頭部進行處理,然後會檢查這份信的拼寫有無錯誤,有的話把錯誤的單詞修改過來,最後會對信的結尾進行處理。這樣一來處理一封信就形成了一條鏈,先處理……,再處理……,最後處理……。
二、責任鏈模式
基於上面提到的場景,UML類圖如下,責任鏈模式之所以能形成鏈,其核心是有一個抽象處理類,該抽象處理類會有一個字段(抽象處理者)被用來記錄後續對象,即記錄執行處理某個步驟的對象。然後該抽象處理類還有一個抽象方法,即handleWork抽象方法,每個處理步驟(鏈節點)都會實現該抽象方法,作爲該步驟的處理邏輯,當然處理完之後還需要丟給下一個鏈節點處理。
- Handler抽象類,即抽象處理類,含有一個字段是抽象處理者successor,當然也有setSuccessor方法,用來設置下一個處理者,即本步驟/鏈節點處理完之後,傳遞給下一個處理者繼續處理。handleWork抽象方法提供了實現不同的處理步驟,有不同的處理邏輯,這裏的handle方法也起到了不少的作用,形成鏈,本鏈節點handleWork之後,判斷自身是否還擁有下一處理者,有的話,下一處理者繼續進行handle->handleWork直到處理結束,無處理者。
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
abstract protected String handleWork(String input);
public String handle(String input) {
//本節點處理完之後返回下一節點
String nextNode = handleWork(input);
if (successor != null) {
return successor.handle(nextNode);
}
return nextNode;
}
}
- AddHeaderHandler類,信的頭部處理器
public class AddHeaderHandler extends Handler{
@Override
protected String handleWork(String input) {
return "From Raoul, Mario and Alan: " + input;
}
}
- CheckSpellHandler類,檢查信的拼寫
public class CheckSpellHandler extends Handler{
@Override
protected String handleWork(String input) {
return input.replaceAll("labda", "lambda");
}
}
- AddFooterHandler類,信的結尾處理器
public class AddFooterHandler extends Handler{
@Override
protected String handleWork(String input) {
return input + " Kind regards";
}
}
public class ChainMain {
public static void main(String[] args) {
AddHeaderHandler addHeaderHandler = new AddHeaderHandler();
CheckSpellHandler checkSpellHandler = new CheckSpellHandler();
AddFooterHandler addFooterHandler = new AddFooterHandler();
addHeaderHandler.setSuccessor(checkSpellHandler);
checkSpellHandler.setSuccessor(addFooterHandler);
String test = addHeaderHandler.handle("labda");
System.out.println(test);
}
}
最後控制檯輸出這封處理過的信:
From Raoul, Mario and Alan: lambda Kind regards
三、責任鏈模式,行爲參數化,lambda方式重構
這裏用了Java8的Function<T,R>函數式接口,方法apply輸入T類型參數,返回R類型,T,R都是String類型,符合我們處理信件,輸入和輸出都是String類型。因爲我們的核心在於處理信件的邏輯,故這裏結合Java8函數式接口,把行爲參數化,實現了處理信件的處理邏輯。
- Function<T,R>
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
- LambdaHandler
public class LambdaHandler {
public static Function<String, String> addHeaderHandler() {
return (input) -> "From Raoul, Mario and Alan: " + input;
}
public static Function<String, String> checkSpellHandler() {
return (input) -> input.replaceAll("labda", "lambda");
}
public static Function<String, String> addFooterHandler() {
return (input) -> input + " Kind regards";
}
}
- ChainMain
public class ChainMain {
public static void main(String[] args) {
Function<String, String> addHeaderHandler = LambdaHandler.addHeaderHandler();
Function<String, String> checkSpellHandler = LambdaHandler.checkSpellHandler();
Function<String, String> addFooterHandler = LambdaHandler.addFooterHandler();
String test = addHeaderHandler.andThen(checkSpellHandler).andThen(addFooterHandler).apply("labda");
System.out.println(test);
}
}
- 最後控制檯輸出這封處理過的信:
From Raoul, Mario and Alan: lambda Kind regards
起核心作用的是Function<T, R>接口的默認方法andThen,除了內置函數式接口Function<T, R>有andThen默認方法,Consumer<T>等內置函數式接口也是提供andThen默認方法的,大部分是能滿足我們的需求的。
我們可以看到,andThen方法的方法參數也是一個跟自身相同的函數式接口Function<T,R>,只不過這裏的泛型有下界通配符和上界通配符Function<? super R, ? extends V> after,當然我們可以暫不用細究這個,因爲我們實現的接口都是入參和返回結果都是String類型。
Objects.requireNonNull(after);首先判斷傳入的參數不能爲空,畢竟這是下一個處理步驟的實現處理邏輯,關鍵是(T t) -> after.apply(apply(t));這句代碼起了作用,這裏分爲兩步,第一步是先執行自身的apply(t)方法,即在當前步驟,先對節點進行處理,然後返回處理結果,接着是對節點(本步驟的處理結果)進行下一步的處理,即after.apply(apply(t))
@FunctionalInterface
public interface Function<T, R> {
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
}
當然節點的最後一個處理步驟,我們需要在最後返回的Function<T,R>在執行一次apply,調用最後一個處理步驟的實現方法,即
String test = addHeaderHandler.andThen(checkSpellHandler).andThen(addFooterHandler).apply("labda");
本文如有不足之處,請指正或者提出好的建議。◕‿◕。謝謝。
參考資料:《Java8實戰》