Menu
CoddyTech

Mẫu Strategy

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

Mẫu chiến lược (Strategy Pattern) xác định một họ thuật toán, đóng gói từng thuật toán và cho phép thay thế chúng cho nhau. Bạn có thể chuyển đổi thuật toán trong khi chương trình đang chạy mà không cần thay đổi đoạn code phía máy khách.

Dưới đây là các lớp chiến lược đơn giản cho những phương thức thanh toán khác nhau:

class CreditCard:
    def pay(self, amount):
        return f"Paid ${amount} with Credit Card"

class PayPal:
    def pay(self, amount):
        return f"Paid ${amount} with PayPal"

class Bitcoin:
    def pay(self, amount):
        return f"Paid ${amount} with Bitcoin"

Mỗi chiến lược đều triển khai cùng một phương thức (pay) nhưng có cách hoạt động khác nhau.

Tạo một lớp ngữ cảnh sử dụng các chiến lược:

class ShoppingCart:
    def __init__(self):
        self.total = 0
        self.payment_strategy = None
    
    def add_item(self, price):
        self.total += price
    
    def set_payment_strategy(self, strategy):
        self.payment_strategy = strategy
    
    def checkout(self):
        return self.payment_strategy.pay(self.total)

Lớp ngữ cảnh có thể chuyển đổi giữa các chiến lược thanh toán khác nhau.

Dùng mẫu chiến lược (strategy pattern):

cart = ShoppingCart()
cart.add_item(50)
cart.add_item(30)

# Dùng chiến lược thanh toán bằng thẻ tín dụng
cart.set_payment_strategy(CreditCard())
print(cart.checkout())

# Chuyển sang chiến lược thanh toán bằng PayPal
cart.set_payment_strategy(PayPal())
print(cart.checkout())

Tạo một ví dụ khác với các chiến lược sắp xếp:

class BubbleSort:
    def sort(self, data):
        return f"Bubble sorted: {sorted(data)}"

class QuickSort:
    def sort(self, data):
        return f"Quick sorted: {sorted(data)}"

class Sorter:
    def __init__(self, strategy):
        self.strategy = strategy
    
    def set_strategy(self, strategy):
        self.strategy = strategy
    
    def sort_data(self, data):
        return self.strategy.sort(data)

# Sử dụng các chiến lược sắp xếp khác nhau
numbers = [3, 1, 4, 1, 5]

sorter = Sorter(BubbleSort())
print(sorter.sort_data(numbers))

sorter.set_strategy(QuickSort())
print(sorter.sort_data(numbers))

Đầu ra:

Paid $80 with Credit Card
Paid $80 with PayPal
Bubble sorted: [1, 1, 3, 4, 5]
Quick sorted: [1, 1, 3, 4, 5]

Điểm chính: Strategy Pattern cho phép bạn thay đổi thuật toán khi chương trình đang chạy. Hãy định nghĩa các chiến lược khác nhau với cùng một giao diện, rồi để lớp ngữ cảnh chọn chiến lược cần dùng. Nhờ vậy, code của bạn linh hoạt và dễ mở rộng bằng các thuật toán mới mà không cần thay đổi code hiện có.

challenge icon

Thử thách

Trung bình

Triển khai một chiến lược BitcoinPayment mới cho hệ thống mua sắm của chúng ta.

Nhiệm vụ của bạn:

  1. Tạo lớp BitcoinPayment triển khai PaymentStrategy
    • Lớp này cần nhận wallet_address trong hàm khởi tạo
    • Phương thức pay cần in chính xác: Paying $X using Bitcoin wallet: Y, trong đó X là số tiền và Y là địa chỉ ví
    • Phương thức pay cần trả về True sau khi in
  2. Viết hàm (function) main để:
    • Tạo một giỏ hàng
    • Thêm một máy tính xách tay giá $1200 và một tai nghe giá $100 vào giỏ hàng
    • Tạo một BitcoinPayment với địa chỉ ví "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
    • Đặt chiến lược thanh toán này cho giỏ hàng
    • Gọi phương thức checkout

Code khởi đầu đã có if __name__ == "__main__": ở cuối. Bạn không cần tự thêm dòng này. Đây là quy ước của Python, đảm bảo hàm main() chỉ chạy khi bạn thực thi trực tiếp tệp (không phải khi một module khác nhập tệp đó). Chỉ cần định nghĩa hàm main(), khối lệnh có sẵn sẽ tự động gọi hàm.

Làm theo cấu trúc tương tự như trong các ví dụ của bài học.

Tự làm thử

from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

class CreditCardPayment(PaymentStrategy):
    def __init__(self, card_number, expiry_date, cvv):
        self.card_number = card_number
        self.expiry_date = expiry_date
        self.cvv = cvv
        
    def pay(self, amount):
        print(f"Paying ${amount} using Credit Card: {self.card_number}")
        return True

class PayPalPayment(PaymentStrategy):
    def __init__(self, email, password):
        self.email = email
        self.password = password
        
    def pay(self, amount):
        print(f"Paying ${amount} using PayPal account: {self.email}")
        return True

class ShoppingCart:
    def __init__(self):
        self.items = []
        self.payment_strategy = None
    
    def add_item(self, item, price):
        self.items.append({"item": item, "price": price})
    
    def set_payment_strategy(self, payment_strategy):
        self.payment_strategy = payment_strategy
    
    def checkout(self):
        total = sum(item["price"] for item in self.items)
        if self.payment_strategy:
            return self.payment_strategy.pay(total)
        else:
            raise ValueError("No payment strategy set")

# Tạo lớp (class) chiến lược BitcoinPayment tại đây


if __name__ == "__main__":
    main()
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 Python online