設計模式---責任鏈模式

2022-11-10 06:01:12

簡述

將各個功能拆分後分別封裝(各功能解耦),需要時可自由組合(包括執行順序)

話不多說,看個優化案例吧。

優化案例

最初版

以下是模擬使用者端想伺服器端傳送請求的業務流程。

使用者端呼叫程式碼如下。

// 使用者端
public class Client {
    public static void main(String[] args) {
        Map<String, String> request = new HashMap<>();
        request.put("username", "admin");
        request.put("password", "admin");
        service(request);
    }
    public static void service(Map<String, String> request) {
        String username = request.get("username");
        String password = request.get("password");
        if (!"admin".equals(username) || !"admin".equals(password)) {
            throw new RuntimeException("使用者名稱或密碼不正確!");
        }
        System.out.println("使用者認證通過。");
        System.out.println("正在處理請求的業務。");
    }
}

如果根據新的需求,需要在使用者認證通過到處理請求的業務之間增加一個快取使用者資訊至Session的處理該怎麼辦,傳統的寫法肯定是直接在上述程式碼的第14行後加入一條快取使用者資訊的處理,但這樣下去,以後每次增加這部分的需求是都無一例外的需要修改使用者端的程式碼,並且實際上使用者端傳送請求的方法肯定不止一處,如果有一個新業務是需要在每個請求的方法中都新增呢。那又怎麼辦?現在上面的程式碼只是模擬,所以只需要加一行列印輸出即可,真實的邏輯可比這複雜得多,到時候的程式碼量也是現在的好幾倍,豈不麻煩死了?

為了解決這個問題,我們可以使用責任鏈模式。

修改版v1

增加一組類,使用責任鏈模式。如下。

// 責任鏈的頂級介面
// 定義責任鏈的核心功能
public interface Chain {
    // 指定下一個處理
    void setNext(Chain next);
    // 處理當前請求
    void handler(Map<String, String> request);
}
// 責任鏈的抽象父類別
// 定義所有責任鏈物件共通的屬性和方法
public abstract class AbstractChain implements Chain {
    // 持有下一個處理
    private Chain next;
    public Chain getNext() {
        return next;
    }
    @Override
    public void setNext(Chain next) {
        this.next = next;
    }
}
// 使用者認證模組
public class AuthChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        String username = request.get("username");
        String password = request.get("password");
        if (!"admin".equals(username) || !"admin".equals(password)) {
            throw new RuntimeException("使用者名稱或密碼不正確!");
        }
        System.out.println("使用者認證通過。");
        Chain next = this.getNext();
        if (next == null) {
            throw new RuntimeException("處理中斷!");
        }
        next.handler(request);
    }
}
// Session快取模組
public class SessionChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        System.out.println("快取使用者資訊至Session。");
        Chain next = this.getNext();
        if (next == null) {
            throw new RuntimeException("處理中斷!");
        }
        next.handler(request);
    }
}
// 業務處理模組
public class ProcessChain extends AbstractChain {
    @Override
    public void handler(Map<String, String> request) {
        System.out.println("正在處理請求的業務。");
    }
}

修改後,使用者端程式碼的呼叫如下。

public class Client {
    public static void main(String[] args) {
        Map<String, String> request = new HashMap<>();
        request.put("username", "admin");
        request.put("password", "admin");
        service(request);
    }
    public static void service(Map<String, String> request) {
        Chain auth = new AuthChain();
        Chain session = new SessionChain();
        Chain process = new ProcessChain();
        auth.setNext(session);
        session.setNext(process);
        auth.handler(request);
    }
}

熟悉資料結構的同學肯定一眼就看出了責任鏈模式的本質:連結串列。將一個冗長的業務處理流程拆分成各個模組,使用時根據業務流程以連結串列的形式將其串聯。不僅提升了各個模組程式碼的複用性,而且還能是的各個模組自由組合,極大的提升了開發效率。

總結

優點

  • 可以自由指定各個模組的執行順序。
  • 各個模組遵循單一職責使得各個模組間解耦。
  • 新增業務流程時只需要增加新的模組即可,遵循開閉原則,提升了系統的可維護性。

缺點

  • 當業務流程過長時,組成的業務鏈也會非常的長,涉及到的物件過多可能會影響系統的效能。
  • 雖然各個模組可以自由組合,但是組合的工作實際上都放在了使用者端,這無疑提升了使用者端的程式碼複雜度。

適用場景

  • 業務流程類的功能都可以使用責任鏈模式。
    • 公司或政府部門各級對於檔案的審批,需要各級各部門蓋章簽字,並且其中一環未通過就得打回重新稽核。