Hướng dẫn Strategy Pattern trong Java
1. Giới thiệu Strategy Pattern
Strategy Pattern là một mẫu thiết kế thuộc nhóm Behavioral Patterns, cho phép định nghĩa một tập hợp các thuật toán, đóng gói từng thuật toán lại và làm cho chúng có thể hoán đổi cho nhau. Strategy Pattern cho phép thuật toán thay đổi độc lập với các client sử dụng nó.
2. Các trường hợp ứng dụng Strategy Pattern
Thay đổi thuật toán linh hoạt: Khi cần thay đổi thuật toán hoặc chiến lược tại thời điểm chạy.
Tách biệt thuật toán: Khi cần tách biệt các thuật toán ra khỏi các lớp chính để dễ bảo trì và mở rộng.
Giảm thiểu điều kiện rẽ nhánh: Khi cần giảm thiểu các khối lệnh if-else hoặc switch-case phức tạp.
3. Triển khai Strategy Pattern
3.1. Ví dụ triển khai đơn giản
Giả sử chúng ta có một hệ thống tính toán chi phí vận chuyển với các chiến lược tính toán khác nhau.
// Bước 1: Tạo interface Strategy
public interface ShippingStrategy {
double calculateShippingCost(double weight);
}
// Bước 2: Tạo các lớp chiến lược cụ thể
public class RegularShipping implements ShippingStrategy {
@Override
public double calculateShippingCost(double weight) {
return weight * 1.5;
}
}
public class ExpressShipping implements ShippingStrategy {
@Override
public double calculateShippingCost(double weight) {
return weight * 2.5;
}
}
// Bước 3: Tạo lớp Context sử dụng Strategy
public class ShippingCostCalculator {
private ShippingStrategy strategy;
public ShippingCostCalculator(ShippingStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(ShippingStrategy strategy) {
this.strategy = strategy;
}
public double calculate(double weight) {
return strategy.calculateShippingCost(weight);
}
}
// Bước 4: Sử dụng Strategy Pattern
public class StrategyPatternDemo {
public static void main(String[] args) {
ShippingCostCalculator calculator = new ShippingCostCalculator(new RegularShipping());
System.out.println("Regular Shipping Cost: " + calculator.calculate(10));
calculator.setStrategy(new ExpressShipping());
System.out.println("Express Shipping Cost: " + calculator.calculate(10));
}
}
3.2. Ví dụ triển khai phức tạp
Giả sử chúng ta có một hệ thống thanh toán với nhiều phương thức thanh toán khác nhau.
// Bước 1: Tạo interface Strategy
public interface PaymentStrategy {
void pay(double amount);
}
// Bước 2: Tạo các lớp chiến lược cụ thể
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String cardHolderName;
public CreditCardPayment(String cardNumber, String cardHolderName) {
this.cardNumber = cardNumber;
this.cardHolderName = cardHolderName;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using Credit Card");
}
}
public class PayPalPayment implements PaymentStrategy {
private String email;
public PayPalPayment(String email) {
this.email = email;
}
@Override
public void pay(double amount) {
System.out.println("Paid " + amount + " using PayPal");
}
}
// Bước 3: Tạo lớp Context sử dụng Strategy
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(double amount) {
paymentStrategy.pay(amount);
}
}
// Bước 4: Sử dụng Strategy Pattern
public class StrategyPatternDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
cart.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456", "John Doe"));
cart.checkout(100);
cart.setPaymentStrategy(new PayPalPayment("john.doe@example.com"));
cart.checkout(200);
}
}
4. Phân tích các trường hợp ứng dụng Strategy Pattern
4.1. Thay đổi thuật toán linh hoạt
Ưu điểm: Cho phép thay đổi thuật toán hoặc chiến lược tại thời điểm chạy một cách dễ dàng và linh hoạt.
Nhược điểm: Cần đảm bảo rằng tất cả các chiến lược được thiết kế một cách nhất quán và không gây lỗi khi hoán đổi.
4.2. Tách biệt thuật toán
Ưu điểm: Giúp tách biệt các thuật toán ra khỏi các lớp chính, dễ dàng bảo trì và mở rộng.
Nhược điểm: Cần thiết kế và quản lý các lớp chiến lược một cách cẩn thận để tránh làm phức tạp hệ thống.
4.3. Giảm thiểu điều kiện rẽ nhánh
Ưu điểm: Giúp giảm thiểu các khối lệnh if-else hoặc switch-case phức tạp, làm mã nguồn rõ ràng và dễ đọc hơn.
Nhược điểm: Cần đảm bảo rằng các lớp chiến lược được thiết kế một cách hợp lý và không làm tăng độ phức tạp của hệ thống.
5. Kết luận
Strategy Pattern là một mẫu thiết kế mạnh mẽ và linh hoạt, giúp tách biệt và hoán đổi các thuật toán hoặc chiến lược một cách dễ dàng trong Java. Việc hiểu và áp dụng đúng Strategy 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.