Strategy

概要

ストラテジーパターンは、アルゴリズムのファミリーを定義し、それぞれを別々のクラスにカプセル化して、互いに交換可能にするデザインパターンです。これにより、アルゴリズムを使用するクライアントと独立してアルゴリズムを変更できます。

使用シーン

  • 複数の類似したクラスが異なる振る舞いを示す場合

  • 実行時にアルゴリズムを選択する必要がある場合

  • 多数の条件分岐を持つメソッドをシンプルにする場合

メリット

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

  • クライアントコードからアルゴリズムの実装が分離される

  • 継承の代わりに委譲を使うことで柔軟性が向上

デメリット

  • クラスの数が多くなりがち

  • 特定のアルゴリズムのみが必要な場合でも、使用しないアルゴリズムについても知っておく必要がある

from abc import ABC, abstractmethod

class Strategy(ABC):
    """アルゴリズムの共通インターフェース"""
    @abstractmethod
    def execute(self, data):
        pass

class ConcreteStrategyA(Strategy):
    """具体的なアルゴリズムA"""
    def execute(self, data):
        return sorted(data)

class ConcreteStrategyB(Strategy):
    """具体的なアルゴリズムB"""
    def execute(self, data):
        return reversed(sorted(data))

class Context:
    """アルゴリズムを使用するクライアントのクラス"""
    def __init__(self, strategy: Strategy):
        self._strategy = strategy

    def set_strategy(self, strategy: Strategy):
        self._strategy = strategy

    def execute_strategy(self, data):
        return self._strategy.execute(data)

# 使用例
data = [23, 12, 78, 34, 56]
context = Context(ConcreteStrategyA())
result = context.execute_strategy(data)
print(list(result))

context.set_strategy(ConcreteStrategyB())
result = context.execute_strategy(data)
print(list(result))


ストラテジーパターンの詳細と適用シナリオ

ストラテジーパターンは、アルゴリズムの動作を実行時に選択できるようにするためのデザインパターンです。具体的なシナリオとしては、異なるタイプの入力に対応する必要があるソフトウェアコンポーネントや、複数の異なるアルゴリズムを柔軟に切り替えることが求められる場合に有効です。

具体的な使用シナリオ: データ分析ツール

データ分析ツールが様々な種類のデータに対して異なる分析アルゴリズムを適用する必要がある場合、ストラテジーパターンを使用することで、クライアントコードの変更を行わずに異なるアルゴリズムを簡単に適用することができます。例えば、統計データに対して平均値、中央値、モードを計算する異なるストラテジーを提供することができます。

サンプルコード(Python)

以下のサンプルコードは、データセットに対して異なる統計計算を適用するストラテジーパターンの実装例です。

from abc import ABC, abstractmethod
import statistics

class StatStrategy(ABC):
    """統計アルゴリズムのためのストラテジー基底クラス"""
    @abstractmethod
    def calculate(self, data):
        pass

class AverageStrategy(StatStrategy):
    """平均値を計算する具体的なストラテジー"""
    def calculate(self, data):
        return statistics.mean(data)

class MedianStrategy(StatStrategy):
    """中央値を計算する具体的なストラテジー"""
    def calculate(self, data):
        return statistics.median(data)

class ModeStrategy(StatStrategy):
    """最頻値を計算する具体的なストラテジー"""
    def calculate(self, data):
        return statistics.mode(data)

class Context:
    """ストラテジーの使用をコントロールするコンテキストクラス"""
    def __init__(self, strategy: StatStrategy):
        self._strategy = strategy

    def set_strategy(self, strategy: StatStrategy):
        self._strategy = strategy

    def execute_strategy(self, data):
        return self._strategy.calculate(data)

# 使用例
data = [1, 2, 2, 3, 4, 5, 5, 5, 6]

context = Context(AverageStrategy())
print("Average:", context.execute_strategy(data))

context.set_strategy(MedianStrategy())
print("Median:", context.execute_strategy(data))

context.set_strategy(ModeStrategy())
print("Mode:", context.execute_strategy(data))

この例では、異なる統計計算のアルゴリズムを各ストラテジークラスにカプセル化しています。Context クラスは実行時にどのストラテジーを使用するかを切り替えることができ、それぞれの計算方法を柔軟に適用できます。

効果的な使用時

  • システムの設計が変更に対して柔軟である必要がある場合

  • 複数のアルゴリズムや振る舞いが同じ問題に対して存在し、それらを簡単に切り替えたい場合

  • アルゴリズムを使うクライアントとアルゴリズム自体を分離したい場合

これらのシナリオでストラテジーパターンを適用することで、ソフトウェアの拡張性と保守性が大きく向上します。



この記事が気に入ったらサポートをしてみませんか?