責任鏈模式與lambda重構責任鏈模式

一、概念以及背景

責任鏈模式(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實戰》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章