Hướng dẫn Command Pattern trong Java
1. Giới thiệu Command Pattern
Command Pattern là một mẫu thiết kế thuộc nhóm Behavioral Patterns, đóng gói một yêu cầu dưới dạng một đối tượng, do đó cho phép tham số hóa các đối tượng khác nhau với các yêu cầu khác nhau, xếp hàng hoặc ghi lại các yêu cầu. Mẫu thiết kế này tách biệt đối tượng gửi yêu cầu từ đối tượng thực hiện yêu cầu, thúc đẩy tính linh hoạt và khả năng mở rộng của hệ thống.
2. Các trường hợp ứng dụng Command Pattern
Thực thi các yêu cầu trong tương lai: Khi cần thực thi các yêu cầu vào một thời điểm khác.
Tạo lịch sử các yêu cầu: Khi cần lưu trữ và có khả năng hoàn tác các yêu cầu.
Xếp hàng và ghi lại các yêu cầu: Khi cần xếp hàng hoặc ghi lại các yêu cầu để thực thi sau.
3. Triển khai Command Pattern
3.1. Ví dụ triển khai đơn giản
Giả sử chúng ta có một đối tượng Light với các hành động on và off. Chúng ta sẽ sử dụng Command Pattern để đóng gói các yêu cầu bật và tắt đèn.
// Bước 1: Tạo interface Command
public interface Command {
void execute();
}
// Bước 2: Tạo các lớp Command cụ thể
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
}
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
}
// Bước 3: Tạo lớp Light
public class Light {
public void on() {
System.out.println("Light is ON");
}
public void off() {
System.out.println("Light is OFF");
}
}
// Bước 4: Tạo lớp Invoker
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
// Bước 5: Sử dụng Command Pattern
public class CommandPatternDemo {
public static void main(String[] args) {
Light light = new Light();
Command lightOn = new LightOnCommand(light);
Command lightOff = new LightOffCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();
remote.setCommand(lightOff);
remote.pressButton();
}
}
3.2. Ví dụ triển khai phức tạp
Giả sử chúng ta có một hệ thống quản lý tài liệu với các hành động như Open, Save, và Close. Chúng ta sẽ sử dụng Command Pattern để đóng gói các yêu cầu này.
// Bước 1: Tạo interface Command
public interface Command {
void execute();
}
// Bước 2: Tạo các lớp Command cụ thể
public class OpenCommand implements Command {
private Document document;
public OpenCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
document.open();
}
}
public class SaveCommand implements Command {
private Document document;
public SaveCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
document.save();
}
}
public class CloseCommand implements Command {
private Document document;
public CloseCommand(Document document) {
this.document = document;
}
@Override
public void execute() {
document.close();
}
}
// Bước 3: Tạo lớp Document
public class Document {
private String name;
public Document(String name) {
this.name = name;
}
public void open() {
System.out.println("Opening document: " + name);
}
public void save() {
System.out.println("Saving document: " + name);
}
public void close() {
System.out.println("Closing document: " + name);
}
}
// Bước 4: Tạo lớp Invoker
public class DocumentInvoker {
private Command openCommand;
private Command saveCommand;
private Command closeCommand;
public DocumentInvoker(Command openCommand, Command saveCommand, Command closeCommand) {
this.openCommand = openCommand;
this.saveCommand = saveCommand;
this.closeCommand = closeCommand;
}
public void openDocument() {
openCommand.execute();
}
public void saveDocument() {
saveCommand.execute();
}
public void closeDocument() {
closeCommand.execute();
}
}
// Bước 5: Sử dụng Command Pattern
public class CommandPatternDemo {
public static void main(String[] args) {
Document document = new Document("MyDocument.txt");
Command openCommand = new OpenCommand(document);
Command saveCommand = new SaveCommand(document);
Command closeCommand = new CloseCommand(document);
DocumentInvoker documentInvoker = new DocumentInvoker(openCommand, saveCommand, closeCommand);
documentInvoker.openDocument();
documentInvoker.saveDocument();
documentInvoker.closeDocument();
}
}
4. Phân tích các trường hợp ứng dụng Command Pattern
4.1. Thực thi các yêu cầu trong tương lai
Ưu điểm: Cho phép lưu trữ và thực thi các yêu cầu vào một thời điểm khác, tăng tính linh hoạt của hệ thống.
Nhược điểm: Cần quản lý các đối tượng Command một cách cẩn thận để tránh rò rỉ bộ nhớ.
4.2. Tạo lịch sử các yêu cầu
Ưu điểm: Giúp lưu trữ lịch sử các yêu cầu, cho phép hoàn tác các hành động nếu cần.
Nhược điểm: Cần lưu trữ thông tin về các Command đã thực hiện, có thể tốn nhiều tài nguyên.
4.3. Xếp hàng và ghi lại các yêu cầu
Ưu điểm: Cho phép xếp hàng hoặc ghi lại các yêu cầu để thực thi sau, giúp quản lý các yêu cầu một cách hiệu quả.
Nhược điểm: Cần đảm bảo rằng các yêu cầu được xếp hàng và ghi lại một cách chính xác và hiệu quả.
5. Kết luận
Command Pattern là một mẫu thiết kế mạnh mẽ và linh hoạt, giúp đóng gói các yêu cầu dưới dạng đối tượng và tách biệt các đối tượng gửi yêu cầu từ các đối tượng thực hiện yêu cầu. Việc hiểu và áp dụng đúng Command Pattern sẽ giúp lập trình viên xây dựng các hệ thống dễ bảo trì, dễ mở rộng và hiệu quả hơn.