1. What (Strategy Pattern là gì?)

Strategy Pattern là một mẫu thiết kế thuộc nhóm Behavioral Design Patterns. Mẫu này cho phép bạn định nghĩa một tập hợp các thuật toán hoặc hành vi, sau đó hoán đổi chúng một cách linh hoạt trong quá trình chạy mà không cần sửa đổi các đối tượng sử dụng các thuật toán đó. Mỗi thuật toán được đóng gói thành một lớp riêng biệt, tuân thủ cùng một giao diện.

2. Why (Tại sao nên dùng Strategy Pattern?)

  • Linh hoạt: Giúp bạn dễ dàng thay đổi hoặc thêm mới các thuật toán mà không cần thay đổi lớp hiện tại.
  • Giảm phụ thuộc: Giúp tách biệt các thuật toán với logic sử dụng chúng, giảm sự phụ thuộc và giúp mã nguồn dễ bảo trì hơn.
  • Tránh lặp lại mã (code duplication): Khi cần thực hiện nhiều phương pháp khác nhau cho một nhiệm vụ (ví dụ nhiều phương thức thanh toán), bạn tránh được việc lặp lại mã qua các câu lệnh if-else hay switch-case.
  • Dễ mở rộng: Bạn có thể thêm nhiều chiến lược mới mà không làm gián đoạn phần mềm hiện có.

3. When (Khi nào nên sử dụng?)

  • Khi bạn có nhiều cách thực hiện một nhiệm vụ và muốn dễ dàng thay đổi hoặc mở rộng các cách đó mà không cần chỉnh sửa logic chính.
  • Khi cần giảm sự phụ thuộc vào một thuật toán cụ thể và muốn tách biệt các thuật toán ra để dễ bảo trì.
  • Khi bạn cần tránh việc sử dụng nhiều câu lệnh điều kiện (if-else hoặc switch-case) để lựa chọn hành vi.

4. How (Cách sử dụng Strategy Pattern trong Java)

Bước 1: Định nghĩa một interface chiến lược (Strategy) để chứa các thuật toán chung.
Bước 2: Tạo các lớp cụ thể (Concrete Strategies) triển khai các thuật toán khác nhau.
Bước 3: Tạo lớp ngữ cảnh (Context) chứa tham chiếu đến đối tượng chiến lược, lớp này sẽ gọi phương thức từ các chiến lược tương ứng.
Bước 4: Trong quá trình chạy, bạn có thể thay đổi chiến lược bằng cách thay thế đối tượng chiến lược trong lớp ngữ cảnh.

5. Ví dụ thực tế: Thanh toán trên Shopee

Trên Shopee, người dùng có thể thanh toán bằng nhiều hình thức như thẻ tín dụng, ví điện tử, chuyển khoản ngân hàng, hay thanh toán khi nhận hàng (COD). Bạn có thể sử dụng Strategy Pattern để linh hoạt lựa chọn các phương thức thanh toán khác nhau mà không cần thay đổi logic chính.

Code ví dụ sử dụng Strategy Pattern trong thanh toán Shopee:

// Bước 1: Tạo giao diện Strategy cho phương thức thanh toán
public interface PaymentStrategy {
    void pay(double amount);
}

// Bước 2: Tạo các lớp cụ thể cho từng phương thức thanh toán

public class CreditCardPayment implements PaymentStrategy {
    private String cardNumber;

    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Thanh toán " + amount + " bằng thẻ tín dụng: " + cardNumber);
    }
}

public class EWalletPayment implements PaymentStrategy {
    private String walletId;

    public EWalletPayment(String walletId) {
        this.walletId = walletId;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Thanh toán " + amount + " bằng ví điện tử: " + walletId);
    }
}

public class BankTransferPayment implements PaymentStrategy {
    private String bankAccount;

    public BankTransferPayment(String bankAccount) {
        this.bankAccount = bankAccount;
    }

    @Override
    public void pay(double amount) {
        System.out.println("Thanh toán " + amount + " bằng chuyển khoản ngân hàng: " + bankAccount);
    }
}

// Bước 3: Tạo lớp Context để chọn phương thức thanh toán

public class PaymentContext {
    private PaymentStrategy paymentStrategy;

    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void executePayment(double amount) {
        paymentStrategy.pay(amount);
    }
}

// Bước 4: Áp dụng Strategy Pattern cho hệ thống thanh toán của Shopee

public class ShopeePaymentSystem {
    public static void main(String[] args) {
        // Thanh toán bằng thẻ tín dụng
        PaymentContext context = new PaymentContext(new CreditCardPayment("4111 1111 1111 1111"));
        context.executePayment(500000);

        // Thanh toán bằng ví điện tử
        context = new PaymentContext(new EWalletPayment("VND123456789"));
        context.executePayment(300000);

        // Thanh toán bằng chuyển khoản ngân hàng
        context = new PaymentContext(new BankTransferPayment("ACB-123456789"));
        context.executePayment(700000);
    }
}

Kết quả:

Thanh toán 500000 bằng thẻ tín dụng: 4111 1111 1111 1111
Thanh toán 300000 bằng ví điện t: VND123456789
Thanh toán 700000 bằng chuyển khoản ngânng: ACB-123456789