Khái niệm

Chain of Responsibility là Pattern thuộc nhóm hành vi (behavioral). Nó cho phép bản chuyển các yêu câu dọc theo chuỗi xử lý. Khi nhận được yêu cầu, mỗi trình xử lý sẽ quyết định xử lý yêu cầu hoặc chuyển cho trình xử lý khác.

Đặt vấn đề

Bây giờ hãy tưởng tượng, bạn là một dev đang làm một hệ thống đặt hàng online. Bạn muốn hạn chế quyền truy cập vào hệ thống, chỉ những người đã xác thực mới có thể tạo đơn hàng. Đối với admin thì có toàn quền truy cập đơn của mọi người.

Sau một thời gian lên ý tưởng, bạn nhận các thao tác xác thực phải thực hiện tuần tự. Hệ thống sẽ xác thực người dùng khi người dùng đăng nhập, Tuy nhiên, nếu thông tin xác thực đó không thành công, thì không có lí do gì để thưc hiện các bước tiếp theo.

Vài tháng sau, chức năng xác thực cần thêm vài case kiểm tra tuần tự nữa.

  • Có một anh đông nghiệp dể thương nào đấy nói với bạn rằng: “Em ơi, em chuyển dữ liệu thổ thẳng database hơi nguy hiểm nha em”. Thế là bạn tiến một bước kiểm tra và lộc lại dữ liệu.
  • Vài tháng yên ắn sau đó, Có anh hacker mũ trắng nào đó bảo hệ thống của bạn quá yếu dể dàng bẻ khóa mật khẩu bằng brute force. Bạn nhận ra đó là sự thật, bạn vội thệm một lớp kiểm tra, lộc các yêu cầu lặp đi lặp lại không thành công nhưng có IP giống nhau và chặn nó.

Theo thời gian, cái lớp xác thực của bạn càng ngày càng bự, nó trở thành mớ hỗn độn khó kiểm soát và mở rộng. Đặc biệt hơn, nếu ở những thành phần khác của hệ thống muốn dùng lại lớp xác thực, đây là điều không thể. Vì nhiều khi ở các thành phần khác chỉ cần sử một vài hàm nhỏ trong cái lớp xác thực bự đó.

Giải pháp

Chain of Responsibility dựa vào việc chuyển đổi các hành vi cụ thể thành các đối tượng hoạt động lập gọi là handlers. Trong vấn đề trên, với hoạt động kiểm thử bạn nên đổi chúng thành một lớp đối tượng cụ thể với một phương thức duy nhất là kiểm tra.

Mô hình gợi ý bạn liên kết các handlers lại thành một chuỗi. Như vậy, mỗi handlers phải lưu trữ tham chiếu đến handler tiếp theo, ngoài việc xử lý yêu cầu handlers còn có nhiệm vụ chuyện đến các handers tiếp theo. Yêu cầu sẽ chuyển theo hết chuỗi hoặc có thể kết thúc bật kì handlers nào.

Cấu Trúc

  • Handler: Định nghĩa 1 interface để xử lý các yêu cầu.
  • ConcreteHandler: Implement phương thức từ handler.
  • Client: Tạo ra các yêu cầu và yêu cầu đó sẽ được gửi đến các đối tượng tiếp nhận.

Ví dụ áp dụng Chain of Responsibility

Test.java

public abstract class Test {
    private Test next;

    public Test linkWith(Test next) {
        this.next = next;
        return next;
    }

    public abstract boolean check(String email, String password);

    
    protected boolean checkNext(String email, String password) {
        if (this.next == null) {
            return true;
        }
        return next.check(email, password);
    }
}

RawDataTest.java

public class RawDataTest extends Test{
    @Override
    public boolean check(String email, String password) {
        System.out.println("Raw Data");
        if (!email.contains("@")) {
            System.out.println("Email Valid");
            return false;
        }
        return this.checkNext(email,password);
    }
}

RoleTest.java

public class RoleTest extends Test {
    @Override
    public boolean check(String email, String password) {
        System.out.println("Role Data");
        if (email.equals("[email protected]")) {
            System.out.println("Hello, admin!");
            return true;
        }
        System.out.println("Hello, user!");
        return checkNext(email, password);
    }
}

UserExistsTest.java

public class UserExistsTest extends Test{
    private Server server;

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

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

        return checkNext(email, password);
    }
}

Server.java

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

public class Server {
    private Map<String, String> users = new HashMap<>();
    private Test test;

    public void setTest(Test test) {
        this.test = test;
    }

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

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

    public boolean logIn(String email, String password) {
        if (this.test.check(email, password)) {
            System.out.println("Authorization have been successful!");

            // Do something useful here for authorized users.

            return true;
        }
        System.out.println("Authorization have been fail!");
        return false;
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) {
        Server server = new Server();
        server.register("[email protected]", "admin_pass");
        server.register("[email protected]", "user_pass");

        Test test = new RawDataTest();
        test.linkWith(new UserExistsTest(server)).linkWith(new RoleTest());

        server.setTest(test);

        server.logIn("[email protected]","user_pass");
        System.out.println();
        server.logIn("phucxample.com","user_pass");
        System.out.println();
        server.logIn("[email protected]", "admin_pass");

    }
}

Khi nào áp dụng

Sử dụng khi chương trình của bạn cần sử nhiều loại yêu cầu khác nhau, nhưng bạn chưa chưa xác định được loại yêu cầu và cách sắp xếp tuần tự của nó.

Áp dụng khi một tác vụ nào đó cần sử lý tuần tự.