0

Design pattern: Chain of Responsibility áp dụng trong dự án như thế nào?

Để thực thi mẫu thiết kế này thì đã có 1 framework hỗ trợ việc xây dựng đó là Apache Commons Chain

Mục đích: hỗ trợ việc xây dựng các ứng dụng có khả năng mở rông cao, nơi các bước xử lý được tổ chức thành một chuỗi (chain) và mỗi bước xử lý là một thành phần có thể tái sử dụng (handler)

Lớp ChainBase là một lớp triển khai mặc định của interface Chain trong Apache Commons Chain. Nó cho phép tạo ra một chuỗi các lệnh (commands), nơi mỗi lệnh trong chuỗi sẽ xử lý một phần của yêu cầu. Một chain có thể được cấu hình để tiếp tục hoặc ngừng xử lý tùy thuộc vào kết quả của mỗi lệnh.

Cách hoạt động của ChainBase: ChainBase implements Chain trong interface Chain có 2 method là addCommand(void) và execute(boolean)

Các thành phần chính:

  1. Command interface: Tất cả các lệnh trong chuỗi đều phải implements interface này. Sau đó @Override lại phương thức excute
  2. Chain interface: đại diện cho danh sách các Command (lệnh) đã được cấu hình, và sẽ được thực thi theo thứ tự để xử lý trên một Context (ngữ cảnh)
  3. ChainBase lớp triển khai Chain và cung cấp logic để thực thi các lệnh theo thứ tự.

Ví dụ sử dụng ChainBase

import org.apache.commons.chain.Command;
import org.apache.commons.chain.Context;
import org.apache.commons.chain.impl.ChainBase;
import org.apache.commons.chain.impl.ContextBase;

public class AuthCommand implements Command {
    @Override
    public boolean execute(Context context) {
        System.out.println("Xác thực request...");
        // Nếu xác thực thất bại, có thể trả về true để dừng chain
        return false; // Tiếp tục chain
    }
}

public class LoggingCommand implements Command {
    @Override
    public boolean execute(Context context) {
        System.out.println("Ghi log request...");
        return false; // Tiếp tục chain
    }
}

public class BusinessLogicCommand implements Command {
    @Override
    public boolean execute(Context context) {
        System.out.println("Xử lý nghiệp vụ...");
        return false; // Tiếp tục chain
    }
}

public class ChainExample {
    public static void main(String[] args) throws Exception {
        // Tạo một context
        Context context = new ContextBase();

        // Tạo một chain
        ChainBase chain = new ChainBase();
        chain.addCommand(new AuthCommand());
        chain.addCommand(new LoggingCommand());
        chain.addCommand(new BusinessLogicCommand());

        // Thực thi chuỗi
        chain.execute(context);
    }
}

Trong dự án thực tế, để được xuyên suốt giữa các Command trong 1 Chain, ta tạo ra 1 class ví dụ là ProcessContext extends ContextBase Thường trong lớp này sẽ có 4 đại diện:

  • Request
  • Response
  • Result
  • Cust
  • và các biến khác tùy chỉnh như: HashMap<String, Object> varRef;

Thay vì cách sử dụng trên ta có thể tách ra nhiều Service và Autowired chúng vào. Đảm bảo tính S trong Solid

@Service
public class DoCheckRefNo {
    public boolean execute(ProcessContext processContext) {
        // Logic kiểm tra RefNo
        System.out.println("Đang kiểm tra RefNo...");
        return processContext.getResult().isOk(); // Ví dụ đơn giản
    }
}

@Service
public class CheckCustomerState {
    public boolean execute(ProcessContext processContext) {
        // Logic kiểm tra trạng thái khách hàng
        System.out.println("Đang kiểm tra trạng thái khách hàng...");
        return processContext.getResult().isOk(); // Ví dụ đơn giản
    }
}

@Service
public class DoCheckMsisdnEmail {
    public boolean execute(ProcessContext processContext) {
        // Logic kiểm tra Msisdn và Email
        System.out.println("Đang kiểm tra Msisdn và Email...");
        return processContext.getResult().isOk(); // Ví dụ đơn giản
    }
}

@Service
public class CheckSRVCPCCDCustomer {
    public boolean execute(ProcessContext processContext) {
        // Logic kiểm tra khách hàng SRVC PCCD
        System.out.println("Đang kiểm tra khách hàng SRVC PCCD...");
        return processContext.getResult().isOk(); // Ví dụ đơn giản
    }
}
@Service
public class CheckProcess {
    private final DoCheckRefNo checkRefNo;
    private final CheckCustomerState checkCustomerState;
    private final DoCheckMsisdnEmail doCheckMsisdnEmail;
    private final CheckSRVCPCCDCustomer checkSRVCPCCDCustomer;

    @Autowired
    public CheckProcess(DoCheckRefNo checkRefNo, 
                        CheckCustomerState checkCustomerState,
                        DoCheckMsisdnEmail doCheckMsisdnEmail,
                        CheckSRVCPCCDCustomer checkSRVCPCCDCustomer) {
        this.checkRefNo = checkRefNo;
        this.checkCustomerState = checkCustomerState;
        this.doCheckMsisdnEmail = doCheckMsisdnEmail;
        this.checkSRVCPCCDCustomer = checkSRVCPCCDCustomer;
    }

    public void executeProcess(ProcessContext processContext) {
        boolean checkResult;

        // Kiểm tra RefNo
        checkResult = checkRefNo.execute(processContext);
        if (!processContext.getResult().isOk()) return;

        // Kiểm tra trạng thái khách hàng
        checkResult = checkCustomerState.execute(processContext);
        if (!processContext.getResult().isOk()) return;

        // Kiểm tra Msisdn và Email
        checkResult = doCheckMsisdnEmail.execute(processContext);
        if (!processContext.getResult().isOk()) return;

        // Kiểm tra khách hàng SRVC PCCD
        checkResult = checkSRVCPCCDCustomer.execute(processContext);
        if (!processContext.getResult().isOk()) return;

        System.out.println("Tất cả các bước kiểm tra đã hoàn thành thành công.");
    }
}

**Và còn 1 kỹ thuật rất hay để gọi Bean mong muốn theo tên và thực thi trong chuỗi này. **

Đó là sử dụng

IExecutor<ProcessContext, Boolean> executor
                    = SpringContext.getBean("_nameBean_");
            executor.execute(context);
            if (!context.getResult().isOk())
                return;
@Service("_nameBean_")
public class Service implements IExecutor<ProcessContext, Boolean> {
    @Override
    public boolean execute(ProcessContext context) throws Exception {
        return true;
    }
}

Kết luận Thay vì sử dụng ChainBase và tự quản lý chuỗi các lệnh (commands), việc sử dụng Spring Boot để tách các bước xử lý thành các service riêng biệt và tiêm chúng vào service quản lý chính thông qua @Autowired là một cách tiếp cận linh hoạt và dễ kiểm thử hơn. Điều này đặc biệt hữu ích trong các ứng dụng Spring Boot có luồng xử lý phức tạp, vì nó dễ bảo trì và dễ mở rộng hơn trong tương lai.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.