職責鏈模式(學習筆記)

  1. 意圖

  使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止java

  2. 動機

  假設如今開發一個在線訂購系統。但願對系統訪問進行限制,只容許認證用戶建立訂單。但是,在接下來的幾個月裏,不斷有新的需求提了出來....緩存

  • 一位同事認爲直接將原始數據傳遞給訂購系統存在安全隱患所以你新增了額外的驗證步驟來清洗請求中的數據
  • 後來,有人注意到系統沒法抵禦暴力密碼破解方式的攻擊爲了防範這種狀況你馬上添加了一個檢查步驟來過濾來自同一IP地址的重複錯誤請求
  • 再後來,又有人提議能夠對包含一樣數據的重複請求返回緩存中的結果從而提升系統響應速度所以你新增了一個檢查步驟確保只有沒有知足條件的緩存結果時請求才能經過並被髮送給系統

  

  檢查代碼原本就已經混亂不堪而每次新增功能都會使其更加臃腫修改某個檢查步驟有時會影響其餘的檢查步驟系統會變得讓人很是費解並且其維護成本也會激增安全

  責任鏈會將特定行爲轉換爲被稱做處理者的獨立對象每一個檢查步驟均可被抽取爲僅有單個方法的類並執行檢查操做請求及其數據則會被做爲參數傳遞給該方法。而且,鏈上的每一個處理者都有一個成員變量來保存對於下一處理者的引用。處理者接收到請求後自行決定是否可以對其進行處理若是本身可以處理處理者就再也不繼續傳遞請求。不然,請求會在鏈上移動 直至全部處理者都有機會對其進行處理。所以在這種狀況下每一個請求要麼最多有一個處理者對其進行處理要麼沒有任何處理者對其進行處理 oop

  3. 適用性

  • 當程序須要使用不一樣方式處理不一樣種類請求,並且請求類型和順序預先未知時。該模式能將多個處理者鏈接成一條鏈。接收到請求後,它會 「詢問」 每一個處理者是否可以對其進行處理。這樣全部處理者都有機會來處理請求
  • 當必須按順序執行多個處理者時
  • 若是所需處理者及其順序必須在運行時進行改變

  4. 結構

      

  5. 效果

  1)單一職責原則 可對發起操做和執行操做的類進行解耦flex

  2)開閉原則 能夠在不更改現有代碼的狀況下在程序中新增處理者ui

  3)加強了給對象指派職責的靈活性this

  4)請求可能一直到鏈的末端都得不處處理spa

  6. 代碼實現

  middleware/Middleware.java: 基礎驗證接口code

package chain_of_responsibility.middleware;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:16
 */
public abstract class Middleware {
    private Middleware next;

    /**
     * Builds chains of middleware objects.
     */
    public Middleware linkWith(Middleware next) {
        this.next = next;
        return next;
    }

    /**
     * Subclasses will implement this method with concrete checks.
     */
    public abstract boolean check(String email, String password);

    /**
     * Runs check on the next object in chain or ends traversing if we're in
     * last object in chain.
     */
    protected boolean checkNext(String email, String password) {
        if (next == null) {
            return true;
        }
        return next.check(email, password);
    }
}

  middleware/ThrottlingMiddleware.java: 檢查請求數量限制component

package chain_of_responsibility.middleware;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:16
 */
public class ThrottlingMiddleware extends Middleware{
    private int requestPerMinute;
    private int request;
    private long currentTime;

    public ThrottlingMiddleware(int requestPerMinute) {
        this.requestPerMinute = requestPerMinute;
        this.currentTime = System.currentTimeMillis();
    }

    /**
     * Please, not that checkNext() call can be inserted both in the beginning
     * of this method and in the end.
     *
     * This gives much more flexibility than a simple loop over all middleware
     * objects. For instance, an element of a chain can change the order of
     * checks by running its check after all other checks.
     */
    public boolean check(String email, String password) {
        if (System.currentTimeMillis() > currentTime + 60_000) {
            request = 0;
            currentTime = System.currentTimeMillis();
        }

        request++;

        if (request > requestPerMinute) {
            System.out.println("Request limit exceeded!");
            Thread.currentThread().stop();
        }
        return checkNext(email, password);
    }
}

  middleware/UserExistsMiddleware.java: 檢查用戶登陸信息

package chain_of_responsibility.middleware;

import chain_of_responsibility.server.Server;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:16
 */
public class UserExistsMiddleware extends Middleware{
    private Server server;

    public UserExistsMiddleware(Server server) {
        this.server = server;
    }

    public boolean check(String email, String password) {
        if (!server.hasEmail(email)) {
            System.out.println("This email is not registered!");
            return false;
        }
        if (!server.isValidPassword(email, password)) {
            System.out.println("Wrong password!");
            return false;
        }
        return checkNext(email, password);
    }
}

  middleware/RoleCheckMiddleware.java: 檢查用戶角色

package chain_of_responsibility.middleware;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:17
 */
public class RockCheckMiddleware extends Middleware{
    public boolean check(String email, String password) {
        if (email.equals("admin@example.com")) {
            System.out.println("Hello, admin!");
            return true;
        }
        System.out.println("Hello, user!");
        return checkNext(email, password);
    }
}

  server/Server.java: 受權目標

package chain_of_responsibility.server;

import chain_of_responsibility.middleware.Middleware;

import java.util.HashMap;
import java.util.Map;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:22
 */
public class Server {
    private Map<String, String> users = new HashMap<>();
    private Middleware middleware;

    /**
     * Client passes a chain of object to server. This improves flexibility and
     * makes testing the server class easier.
     */
    public void setMiddleware(Middleware middleware) {
        this.middleware = middleware;
    }

    /**
     * Server gets email and password from client and sends the authorization
     * request to the chain.
     */
    public boolean logIn(String email, String password) {
        if (middleware.check(email, password)) {
            System.out.println("Authorization have been successful!");

            // Do something useful here for authorized users.

            return true;
        }
        return false;
    }

    public void register(String email, String password) {
        users.put(email, password);
    }

    public boolean hasEmail(String email) {
        return users.containsKey(email);
    }

    public boolean isValidPassword(String email, String password) {
        return users.get(email).equals(password);
    }
}

  Demo.java: 客戶端代碼

package chain_of_responsibility;

import chain_of_responsibility.middleware.Middleware;
import chain_of_responsibility.middleware.RockCheckMiddleware;
import chain_of_responsibility.middleware.ThrottlingMiddleware;
import chain_of_responsibility.middleware.UserExistsMiddleware;
import chain_of_responsibility.server.Server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author GaoMing
 * @date 2021/7/21 - 21:15
 */
public class Demo {
    private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    private static Server server;

    private static void init() {
        server = new Server();
        server.register("admin@example.com", "admin_pass");
        server.register("user@example.com", "user_pass");

        // All checks are linked. Client can build various chains using the same
        // components.
        Middleware middleware = new ThrottlingMiddleware(2);
        middleware.linkWith(new UserExistsMiddleware(server))
                .linkWith(new RockCheckMiddleware());

        // Server gets a chain from client code.
        server.setMiddleware(middleware);
    }

    public static void main(String[] args) throws IOException {
        init();

        boolean success;
        do {
            System.out.print("Enter email: ");
            String email = reader.readLine();
            System.out.print("Input password: ");
            String password = reader.readLine();
            success = server.logIn(email, password);
        } while (!success);
    }
}

  執行結果

Enter email: admin@example.com
Input password: admin_pass
Hello, admin!
Authorization have been successful!


Enter email: user@example.com
Input password: user_pass
Hello, user!
Authorization have been successful!

 

  7. 與其餘模式的關係

  • 責任鏈模式、命令模式、中介者模式和觀察者模式用於處理請求發送者和接收者之間的不一樣鏈接方式:
    1. 責任鏈按照順序將請求動態傳遞給一系列的潛在接收者,直至其中一名接收者對請求進行處理
    2. 命令在發送者和請求者之間創建單向鏈接
    3. 中介者清除了發送者和請求者之間的直接鏈接, 強制它們經過一箇中介對象進行間接溝通
    4. 觀察者容許接收者動態地訂閱或取消接收請求

  • 責任鏈一般和組合模式結合使用。在這種狀況下,葉組件接收到請求後,能夠將請求沿包含全體父組件的鏈一直傳遞至對象樹的底部

  例如,當用戶點擊按鈕時,按鈕產生的事件將沿着GUI元素鏈進行傳遞,最開始是按鈕的容器(如窗體或面板),直至應用程序主窗口。鏈上第一個能處理該事件的元素會對其進行處理     

  •  在責任鏈中,可使用命令模式封裝請求    

  8. 已知應用  

  使用示例:責任鏈模式在Java程序中並不常見,由於它僅在代碼與對象鏈打交道時才能發揮做用。該模式最流行的使用案例之一是在GUI類中將事件向上傳遞給父組件。另外一個值得注意的使用案例是依次訪問過濾器
  下面是該模式在覈心 Java 程序庫中的一些示例:

  • javax.servlet.Filter#doFilter()
  • java.util.logging.Logger#log()

  識別方法:該模式可經過一組對象的行爲方法間接調用其餘對象的相同方法來識別,並且全部對象都會遵循相同的接口

相關文章
相關標籤/搜索