Strategy パターン
CoddyのPythonジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 48/64。
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"各ストラテジーは、同じメソッド(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を使うと、実行時にアルゴリズムを切り替えられます。同じインターフェースを持つ複数のストラテジーを定義し、コンテキストクラスにどれを使うか選ばせます。これにより、既存のコードを変更せずに新しいアルゴリズムを追加でき、コードの柔軟性と拡張性が高まります。
チャレンジ
中級ショッピングシステムに新しいBitcoinPaymentストラテジーを実装しましょう。
あなたの課題は次のとおりです。
- PaymentStrategyを実装するBitcoinPaymentクラスを作成する
- コンストラクターでwallet_addressを受け取るようにします
- payメソッドは次の内容を正確に出力する必要があります。
Paying $X using Bitcoin wallet: Y。ここで、Xは金額、Yはウォレットアドレスです - 出力後、payメソッドはTrueを返す必要があります
- 次の処理を行うmain関数を書く
- ショッピングカートを作成する
- 価格が$1200のノートパソコンと、価格が$100のヘッドホンをカートに追加する
- ウォレットアドレスが "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" のBitcoinPaymentを作成する
- この支払いストラテジーをカートに設定する
- checkoutメソッドを呼び出す
スターターコードには、末尾にif __name__ == "__main__":がすでに含まれています。自分で追加する必要はありません。これは、ファイルが直接実行されたとき(別のモジュールからインポートされたときではなく)にのみmain()関数が実行されるようにするPythonの慣習です。main()関数を定義するだけで、用意されているブロックが自動的に呼び出します。
レッスンの例と同じ構造に従ってください。
自分で試してみよう
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()このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Pythonオンラインコンパイラ