Giới thiệu mẫu thiết kế Chain of Responsibility
Mẫu thiết kế Chain of Responsibility (Chuỗi Trách Nhiệm) là một trong những mẫu thiết kế phần mềm phổ biến, giúp xây dựng các ứng dụng mở rộng dễ dàng. Mẫu thiết kế này cho phép tổ chức các bước xử lý thành một chuỗi, với mỗi bước (handler) có thể tái sử dụng và có thể xử lý yêu cầu thông qua việc truyền thông tin giữa các bước.
Một trong những công cụ hữu ích để thực thi mẫu thiết kế này là Apache Commons Chain, một framework hỗ trợ việc xây dựng chuỗi xử lý trong Java.
Mục đích của Apache Commons Chain
Framework này hỗ trợ phát triển các ứng dụng có khả năng mở rộng cao, nơi mà các bước xử lý được liên kết chặt chẽ với nhau, cho phép ứng dụng dễ dàng được mở rộng và bảo trì.
Các thành phần chính của Apache Commons Chain
Trong Apache Commons Chain, lớp ChainBase
là cốt lõi, cung cấp triển khai mặc định cho interface Chain
. Nó cho phép chúng ta tạo ra chuỗi các lệnh (commands), trong đó mỗi lệnh 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 dừng xử lý tùy thuộc vào kết quả của từng lệnh.
1. Interface Command
Tất cả các lệnh trong chuỗi cần phải triển khai interface này và ghi đè phương thức execute
.
2. Interface Chain
Interface này đại diện cho danh sách các Command
đã được cấu hình và sẽ thực thi theo thứ tự, xử lý trên một ngữ cảnh nhất định (Context).
3. Lớp ChainBase
ChainBase triển khai Chain
và cung cấp logic để thực thi các lệnh theo thứ tự đã định.
Ví dụ sử dụng ChainBase
Dưới đây là một ví dụ cụ thể để minh họa cách sử dụng ChainBase:
java
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 ví dụ trên, chúng ta đã tạo ra các lệnh khác nhau như AuthCommand
, LoggingCommand
và BusinessLogicCommand
. Mỗi lệnh này xử lý một nhiệm vụ riêng biệt trong quá trình xử lý yêu cầu.
Xây dựng ProcessContext
Trong một dự án thực tế, để quản lý tốt hơn giữa các Command trong một chain, chúng ta có thể tạo ra một lớp ProcessContext
kế thừa từ ContextBase
. Trong lớp này, chúng ta có thể định nghĩa các thuộc tính cần thiết như:
- Request
- Response
- Result
- Cust
- Và các biến tùy chỉnh khác, chẳng hạn như `HashMap<String, Object> varRef;
Tách bạch các Service
Để đảm bảo tính SOLID, chúng ta có thể tách các bước xử lý thành các dịch vụ riêng biệt và sử dụng Spring Boot để tiêm chúng vào dịch vụ chính thông qua annotation @Autowired
. Dưới đây là một ví dụ:
java
@Service
public class DoCheckRefNo {
public boolean execute(ProcessContext processContext) {
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) {
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) {
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) {
System.out.println("Đang kiểm tra khách hàng SRVC PCCD...");
return processContext.getResult().isOk(); // Ví dụ đơn giản
}
}
Lớp chính CheckProcess
sẽ quản lý việc thực hiện các lệnh này:
java
@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.");
}
}
Kỹ thuật gọi Bean theo tên trong chuỗi
Chúng ta cũng có thể sử dụng một kỹ thuật thú vị để gọi Bean mong muốn theo tên và thực thi trong chuỗi này. Điều này giúp cải thiện tính linh hoạt trong quản lý các service:
java
IExecutor<ProcessContext, Boolean> executor = SpringContext.getBean("_nameBean_");
executor.execute(context);
if (!context.getResult().isOk()) return;
java
@Service("_nameBean_")
public class Service implements IExecutor<ProcessContext, Boolean> {
@Override
public boolean execute(ProcessContext context) throws Exception {
return true;
}
}
Kết luận
Việc áp dụng mẫu thiết kế Chain of Responsibility thông qua Apache Commons Chain mang lại nhiều lợi ích cho việc tổ chức và quản lý các bước xử lý trong ứng dụng. Tuy nhiên, việc sử dụng Spring Boot để tách các dịch vụ thành các service riêng biệt và tiêm chúng vào dịch vụ quản lý chính thông qua @Autowired
là một cách tiếp cận tối ưu hơn. Cách này không chỉ giúp mã nguồn trở nên dễ bảo trì mà còn dễ dàng mở rộng trong tương lai. Điều này đặc biệt hữu ích cho các ứng dụng với luồng xử lý phức tạp.
source: viblo