Menu
CoddyTech

Mẫu Strategy

Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 69 trên 87.

Mẫu Strategy là một mẫu thiết kế hành vi cho phép bạn định nghĩa một nhóm thuật toán, đóng gói từng thuật toán trong lớp riêng và thay thế chúng cho nhau. Thay vì cố định một hành vi cụ thể trong code, bạn có thể thay đổi thuật toán khi chương trình đang chạy mà không cần sửa code sử dụng chúng.

Hãy hình dung một hệ thống thanh toán hỗ trợ nhiều phương thức thanh toán. Nếu không dùng Strategy Pattern, bạn sẽ phải xử lý những câu lệnh điều kiện rối rắm. Với mẫu này, mỗi phương thức thanh toán trở thành một chiến lược riêng:

interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via PayPal");
    }
}

Lớp context giữ tham chiếu đến một chiến lược và ủy quyền công việc cho chiến lược đó. Điểm mấu chốt là context không biết mình đang dùng chiến lược cụ thể nào. Nó chỉ biết giao diện:

class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

Giờ bạn có thể thay đổi hành vi thanh toán một cách linh hoạt:

ShoppingCart cart = new ShoppingCart();

cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100);  // Đã thanh toán 100 bằng thẻ tín dụng

cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50);   // Đã thanh toán 50 qua PayPal

Mẫu chiến lược (Strategy Pattern) phát huy hiệu quả khi bạn có nhiều cách thực hiện một hành động và muốn dễ dàng chuyển đổi giữa chúng. Để thêm phương thức thanh toán mới, bạn chỉ cần tạo một lớp mới triển khai PaymentStrategy: không cần thay đổi code hiện có.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống định dạng văn bản bằng mẫu Strategy! Bạn sẽ tạo một hệ thống cho phép thay đổi các chiến lược định dạng khác nhau trong lúc chương trình chạy để biến đổi văn bản theo nhiều cách, rất phù hợp với ứng dụng cần hỗ trợ nhiều định dạng đầu ra.

Bạn sẽ tổ chức code trong bốn tệp:

  • TextFormatter.java: Định nghĩa giao diện TextFormatter mà tất cả chiến lược định dạng sẽ triển khai. Giao diện này cần khai báo một phương thức duy nhất là String format(String text), nhận một chuỗi và trả về phiên bản đã định dạng.
  • Formatters.java: Tạo ba chiến lược cụ thể triển khai giao diện TextFormatter:

    UpperCaseFormatter - chuyển văn bản thành chữ in hoa.

    LowerCaseFormatter - chuyển văn bản thành chữ thường.

    TitleCaseFormatter - viết hoa ký tự đầu tiên của mỗi từ và chuyển các ký tự còn lại thành chữ thường. Các từ được ngăn cách bằng dấu cách.

  • TextEditor.java: Tạo lớp ngữ cảnh sử dụng một chiến lược định dạng. TextEditor cần có:

    Một trường private để lưu chiến lược TextFormatter hiện tại.

    Một phương thức setFormatter(TextFormatter formatter) để thay đổi chiến lược định dạng đang hoạt động.

    Một phương thức publishText(String text) áp dụng bộ định dạng hiện tại cho văn bản và in ra Published: [formatted text]. Nếu chưa đặt bộ định dạng nào, phương thức sẽ in nguyên văn Published: [original text].

  • Main.java: Kết hợp hệ thống chiến lược của bạn! Bạn sẽ nhận hai đầu vào: văn bản cần định dạng (String) và kiểu định dạng (String: "upper", "lower" hoặc "title").

    Tạo một TextEditor và gọi publishText với văn bản đầu vào trước khi đặt bộ định dạng nào, để thể hiện hành vi mặc định.

    Sau đó, dựa trên đầu vào kiểu định dạng, đặt chiến lược định dạng phù hợp và gọi lại publishText với cùng văn bản để hiển thị kết quả đã định dạng.

Bạn sẽ nhận hai đầu vào theo thứ tự: văn bản cần định dạng (String) và kiểu định dạng (String).

Ví dụ, với đầu vào hello world và title, đầu ra sẽ là:

Published: hello world
Published: Hello World

Lưu ý rằng TextEditor không cần biết gì về logic định dạng cụ thể. Nó chỉ chuyển việc xử lý cho chiến lược hiện được đặt. Nhờ vậy, bạn có thể dễ dàng thêm kiểu định dạng mới mà không cần chỉnh sửa code của trình soạn thảo!

Tự làm thử

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String text = scanner.nextLine();
        String style = scanner.nextLine();
        
        // TODO: Tạo một instance của TextEditor
        
        // TODO: Gọi publishText với văn bản đầu vào (trước khi thiết lập bất kỳ bộ định dạng nào)
        
        // TODO: Dựa vào kiểu định dạng đầu vào ("upper", "lower" hoặc "title"),
        // thiết lập bộ định dạng phù hợp và gọi lại publishText
        
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Java online