Menu
Coddy logo textTech

Strategy パターン

CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 48/64。

ストラテジーパターンは、一連のアルゴリズムを定義し、それぞれをカプセル化して、それらを交換可能にします。ストラテジーパターンを利用することで、クライアント側のコードを変更することなく、実行時にアルゴリズムを切り替えることができます。

以下は、さまざまな支払い方法のためのシンプルなストラテジークラスです:

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"

各ストラテジーは同じメソッド (pay) を実装しますが、その振る舞いは異なります。

ストラテジーを使用するコンテキストクラスを作成します:

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)

コンテキストクラスは、異なる支払い戦略を切り替えることができます。

ストラテジーパターンを使用します:

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

# クレジットカードのストラテジーを使用
cart.set_payment_strategy(CreditCard())
print(cart.checkout())

# PayPalのストラテジーに切り替え
cart.set_payment_strategy(PayPal())
print(cart.checkout())

ソート戦略を用いた別の例を作成しましょう:

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)

# 異なるソート戦略を使用する
numbers = [3, 1, 4, 1, 5]

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

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

出力:

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

重要なポイント:ストラテジーパターン(Strategy Pattern)を使用すると、実行時にアルゴリズムを入れ替えることができます。同じインターフェースを持つ異なるストラテジーを定義し、コンテキストクラスに使用するものを選択させます。これにより、既存のコードを変更することなく、新しいアルゴリズムでコードを柔軟かつ簡単に拡張できるようになります。

challenge icon

チャレンジ

中級

ショッピングシステムに新しい BitcoinPayment ストラテジーを実装してください。

あなたのタスクは以下の通りです:

  1. PaymentStrategy を実装する BitcoinPayment クラスを作成する
    • コンストラクタで wallet_address を受け取るようにします
    • pay メソッドは、正確に Paying $X using Bitcoin wallet: Y と出力する必要があります(X は金額、Y はウォレットアドレスです)
    • pay メソッドは、出力した後に True を返す必要があります
  2. 以下の処理を行う main 関数を記述する:
    • ショッピングカートを作成する
    • 1200ドルの "Laptop" と100ドルの "Headphones" をカートに追加する
    • ウォレットアドレス "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" を使用して BitcoinPayment を作成する
    • この支払いストラテジーをカートに設定する
    • checkout メソッドを呼び出す

スターターコードの最後には既に if __name__ == "__main__": が含まれているため、自分で追加する必要はありません。これは、ファイルが直接実行された場合にのみ main() 関数が実行されるようにする(別のモジュールによってインポートされた場合には実行されないようにする)Python の慣習です。単に main() 関数を定義すれば、提供されているブロックが自動的にそれを呼び出します。

レッスンの例で示されたものと同じ構造に従ってください。

チートシート

ストラテジーパターン(Strategy Pattern)は、一連のアルゴリズムを定義し、それぞれをカプセル化して、それらを交換可能にします。クライアント側のコードを変更することなく、実行時にアルゴリズムを切り替えることができます。

同じインターフェースを持つストラテジークラスを作成します:

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"

ストラテジーを使用するコンテキストクラスを作成します:

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)

ストラテジーパターンを使用します:

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

# Use credit card strategy
cart.set_payment_strategy(CreditCard())
print(cart.checkout())

# Switch to PayPal strategy
cart.set_payment_strategy(PayPal())
print(cart.checkout())

ソートストラテジーを使用した別の例:

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)

自分で試してみよう

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")

# ここにBitcoinPaymentストラテジークラスを作成してください


if __name__ == "__main__":
    main()
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン