Design Pattern Command là một trong những mẫu thiết kế hành vi (Behavioral Design Pattern), cho phép bạn đóng gói một yêu cầu dưới dạng một đối tượng, từ đó bạn có thể:
- Tham số hóa các đối tượng với các yêu cầu khác nhau.
- Xếp hàng hoặc lưu trữ lịch sử các lệnh (ví dụ: undo/redo).
- Tách người gửi lệnh (invoker) khỏi người thực thi lệnh (receiver).
Cấu trúc của Command Pattern
Command pattern gồm các thành phần:
-
Command (interface):
- Khai báo phương thức
execute()
.
- Khai báo phương thức
-
ConcreteCommand:
- Cài đặt interface Command, liên kết với Receiver.
- Gọi phương thức cụ thể trên Receiver trong
execute()
.
-
Receiver:
- Thực thi hành động thực tế.
-
Invoker:
- Gọi
command.execute()
khi cần.
- Gọi
-
Client:
- Tạo command, gán receiver, và giao cho invoker thực hiện.
Ví dụ đơn giản bằng Java
// Command
public interface Command { void execute();
} // Receiver
public class Light { public void turnOn() { System.out.println("Light is ON"); } public void turnOff() { System.out.println("Light is OFF"); }
} // Concrete Command
public class TurnOnLightCommand implements Command { private Light light; public TurnOnLightCommand(Light light) { this.light = light; } public void execute() { light.turnOn(); }
} // Invoker
public class RemoteControl { private Command command; public void setCommand(Command command) { this.command = command; } public void pressButton() { command.execute(); }
} // Client
public class CommandPatternDemo { public static void main(String[] args) { Light light = new Light(); Command lightOn = new TurnOnLightCommand(light); RemoteControl remote = new RemoteControl(); remote.setCommand(lightOn); remote.pressButton(); // Output: Light is ON }
}
Ứng dụng thực tế trong Spring Boot
Bối cảnh: Mở tài khoản ngân hàng online
Quy trình gồm 3 bước:
- Kiểm tra giấy tờ tùy thân
- Kiểm tra độ tuổi
- Gửi email xác nhận
Cách triển khai Command Pattern kết hợp Chain of Responsibility (cũng là 1 Behavioral pattern), thường được dùng trong xử lý nghiệp vụ tuần tự
1. Interface Command chung:
public interface Command { boolean execute(Context context);
}
2. Context dùng để chia sẻ dữ liệu giữa các bước:
public class Context { public String customerName; public int age; public boolean hasValidId; public boolean resultOk = true; public String message;
}
3. Các bước xử lý cụ thể (command):
// Kiểm tra giấy tờ:
public class CheckIdCommand implements Command { public boolean execute(Context context) { if (!context.hasValidId) { context.resultOk = false; context.message = "ID is not valid."; return false; } return true; }
} // Kiểm tra tuổi:
public class CheckAgeCommand implements Command { public boolean execute(Context context) { if (context.age < 18) { context.resultOk = false; context.message = "Customer must be at least 18 years old."; return false; } return true; }
} // Gửi email
public class SendEmailCommand implements Command { public boolean execute(Context context) { System.out.println("Sending email to " + context.customerName); return true; }
}
4. CommandExecutor – nơi chạy toàn bộ quy trình:
public class CommandExecutor { private final List<Command> commands; public CommandExecutor(List<Command> commands) { this.commands = commands; } public void execute(Context context) { for (Command command : commands) { if (!command.execute(context)) { System.out.println("Stopped: " + context.message); return; } } System.out.println("All steps completed successfully."); }
}
5 Sử dụng
public class Main { public static void main(String[] args) { Context ctx = new Context(); ctx.customerName = "Nguyen Van A"; ctx.age = 17; ctx.hasValidId = true; List<Command> commandList = List.of( new CheckIdCommand(), new CheckAgeCommand(), new SendEmailCommand() ); CommandExecutor executor = new CommandExecutor(commandList); executor.execute(ctx); }
}
Kết quả:
Stopped: Customer must be at least 18 years old. // Nếu sửa lại tuổi = 20
Sending email to Nguyen Van A
All steps completed successfully.
Các bạn có thể sử dụng thư viện commons-chain
của Apache để không cần phải triển khai. Mình sẽ sử dụng nó trong bài Chain of Responsibility Pattern nhé.