KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

(オススメ3) Python基礎 オブジェクト指向10(Strategy) 振る舞いに関するデザインパターン

登録日 :2025/10/02 20:48
カテゴリ :Python基礎

Strategy

複数のアルゴリズムを別個のクラスとして定義し、切り替えができるようにするパターン

  • 親クラスでクライアントにアクセスさせるための共通APIを定義する
  • 子クラスで具体的なアルゴリズムを実装する

構成要素

Strategy

  • インターフェースもしくは抽象クラス
  • それぞれのアルゴリズムに共通のAPIを定義

ConcreteStrategy

  • Strategyを継承(実装)する子クラス
  • Strategyで定義された共通APIを満たすように具体的なアルゴリズムを実装する

Context

  • アルゴリズムの利用者
  • Strategy型のオブジェクトを内部に保持する
  • 具体的な処理はStrategyオブジェクトに移譲する

オブジェクト指向的要素

「継承」「ポリモーフィズム」「移譲」を利用したパターン

  • StrategyとConcreteStrategyは継承(実装)関係
  • Strategyクラスで共通のAPIを定義し、クライアントはその共通APIを利用することで、具体的なクラスの切り替えが可能になる。
  • ContextクラスはStrategyオブジェクトに処理を移譲する

メリット・デメリット

メリット

  • 実行時にオブジェクト内で使用されるアルゴリズムを交換できる

  • アルゴリズムの実装の詳細を利用側のコードから分離できる

  • アルゴリズムの追加が容易になる

デメリット

  • アルゴリズムの種類が少ない場合、過剰な設計となる可能性がある。

使い所

ある問題を解決する方法が複数あり、プログラム実行時に動的に切り替えたい場合

  • Context に渡すConcreteStrategyによってアルゴリズムが切り替わる

クラス内にアルゴリズムを切り替えるために多くの条件文がある場合

  • 条件分岐を共通なAPIを持った個別のConcreteStrategyクラスに切り出している

サンプルコード

複数の支払い方法が選択可能なショッピングカート

from abc import ABCMeta, abstractmethod


class PaymentStrategy(metaclass=ABCMeta):
    """Strategy Class"""
    @abstractmethod
    def pay(self, _amount: int):
        pass


class CreditCardPaymentStrategy(PaymentStrategy):
    """Concrete Strategy"""
    def pay(self, _amount: int):
        print(f{_amount} paid by Credit card.")


class CashPaymentStrategy(PaymentStrategy):
    """Concrete Strategy"""
    def pay(self, _amount: int):
        print(f{_amount} paid by Cash.")


class ShoppingCart:
    """Context"""
    def __init__(self):
        self.__total = 0
        self.__items = []

    def add_item(self, _item, _price: int):
        self.__total += _price
        self.__items.append((_item, _price))

    def pay(self, payment_strategy: PaymentStrategy):
        payment_strategy.pay(self.__total)


if __name__ == '__main__':
    cart = ShoppingCart()
    cart.add_item('mouse', 500)
    cart.add_item('keyboard', 1000)

    payment_credit = CreditCardPaymentStrategy()
    payment_cash = CashPaymentStrategy()

    cart.pay(payment_credit)
    cart.pay(payment_cash)