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:
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 excuteChain
interface: đại diện cho danh sách cácCommand
(lệnh) đã được cấu hình, và sẽ được thực thi theo thứ tự để xử lý trên mộtContext
(ngữ cảnh)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