StrategyパターンとFactory Methodパターン：総合的な比較と実践的活用

## はじめに
ソフトウェア設計において、StrategyパターンとFactory Methodパターンは頻繁に使用される重要なデザインパターンです。本記事では、これらのパターンの特徴、使用例、比較、そして関連性について総合的に解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/d687c374-f871-c44f-7516-6ab4b35db33f.png)


## 目次

1. [なぜStrategyとFactory Methodを比較するのか](#なぜStrategyとFactory-Methodを比較するのか)
2. [Strategyパターン](#Strategyパターン)
3. [Factory Methodパターン](#Factory-Methodパターン)
4. [パターンの比較](#パターンの比較)
5. [パターンの組み合わせ](#パターンの組み合わせ)
6. [他のデザインパターンとの関連](#他のデザインパターンとの関連)
7. [まとめ](#まとめ)

## なぜStrategyとFactory Methodを比較するのか

1. **使用頻度**: 両パターンとも実務でよく使用され、多くの開発者が日常的に遭遇します。

2. **目的の類似性**: 両パターンとも、オブジェクト指向設計の柔軟性を高めることを目的としています。

3. **適用範囲の違い**: Strategyは振る舞いの変更、Factory Methodはオブジェクト生成に焦点を当てており、適用範囲が異なります。

4. **組み合わせの可能性**: これらのパターンは、しばしば組み合わせて使用されることがあります。

5. **設計の選択**: パターンの特徴を理解し比較することで、適切な設計選択ができるようになります。

## Strategyパターン

Strategyパターンは、アルゴリズムの族を定義し、それぞれをカプセル化して交換可能にするパターンです。

### クラス図

```mermaid
classDiagram
    class Context {
        -strategy: Strategy
        +setStrategy(Strategy)
        +executeStrategy()
    }
    class Strategy {
        <<interface>>
        +execute()
    }
    class ConcreteStrategyA {
        +execute()
    }
    class ConcreteStrategyB {
        +execute()
    }
    Context o-- Strategy
    Strategy <|.. ConcreteStrategyA
    Strategy <|.. ConcreteStrategyB
```

### 実装例：支払い方法

```mermaid
classDiagram
    class PaymentStrategy {
        <<interface>>
        +pay(amount: float): None
    }
    class CreditCardPayment {
        +pay(amount: float): None
    }
    class PayPalPayment {
        +pay(amount: float): None
    }
    class Payment {
        -strategy: PaymentStrategy
        +process_payment(amount: float): None
        +set_strategy(strategy: PaymentStrategy): None
    }
    PaymentStrategy <|.. CreditCardPayment
    PaymentStrategy <|.. PayPalPayment
    Payment o-- PaymentStrategy
```

```mermaid
sequenceDiagram
    participant Client
    participant Payment
    participant CreditCardPayment
    participant PayPalPayment

    Client->>Payment: new(CreditCardPayment)
    activate Payment
    Payment->>CreditCardPayment: new
    Payment-->>Client: payment
    deactivate Payment

    Client->>Payment: process_payment(1000)
    activate Payment
    Payment->>CreditCardPayment: pay(1000)
    activate CreditCardPayment
    CreditCardPayment-->>Payment: 
    deactivate CreditCardPayment
    Payment-->>Client: 
    deactivate Payment

    Client->>Payment: set_strategy(PayPalPayment)
    activate Payment
    Payment->>PayPalPayment: new
    Payment-->>Client: 
    deactivate Payment

    Client->>Payment: process_payment(2000)
    activate Payment
    Payment->>PayPalPayment: pay(2000)
    activate PayPalPayment
    PayPalPayment-->>Payment: 
    deactivate PayPalPayment
    Payment-->>Client: 
    deactivate Payment
```

```python
from abc import ABC, abstractmethod

class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float) -> None:
        pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount: float) -> None:
        print(f"クレジットカードで{amount}円支払いました。")

class PayPalPayment(PaymentStrategy):
    def pay(self, amount: float) -> None:
        print(f"PayPalで{amount}円支払いました。")

class Payment:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def process_payment(self, amount: float) -> None:
        self.strategy.pay(amount)

    def set_strategy(self, strategy: PaymentStrategy) -> None:
        self.strategy = strategy

# 使用例
if __name__ == "__main__":
    payment = Payment(CreditCardPayment())
    payment.process_payment(1000)
    
    payment.set_strategy(PayPalPayment())
    payment.process_payment(2000)
```

### 実行結果

```
クレジットカードで1000円支払いました。
PayPalで2000円支払いました。
```

### 解説

- `PaymentStrategy`は抽象基底クラスで、具体的な支払い方法はこれを継承して実装します。
- `Payment`クラス（コンテキスト）は`PaymentStrategy`を保持し、必要に応じて切り替えることができます。
- このパターンにより、新しい支払い方法の追加が容易になり、既存のコードを変更せずに支払いロジックを拡張できます。

## Factory Methodパターン

Factory Methodパターンは、オブジェクトの作成をサブクラスに委ねるパターンです。

### クラス図

```mermaid
classDiagram
    class Creator {
        <<abstract>>
        +factoryMethod(): Product
        +someOperation()
    }
    class ConcreteCreatorA {
        +factoryMethod(): Product
    }
    class ConcreteCreatorB {
        +factoryMethod(): Product
    }
    class Product {
        <<interface>>
        +operation()
    }
    class ConcreteProductA {
        +operation()
    }
    class ConcreteProductB {
        +operation()
    }
    Creator <|-- ConcreteCreatorA
    Creator <|-- ConcreteCreatorB
    Product <|.. ConcreteProductA
    Product <|.. ConcreteProductB
    Creator ..> Product
    ConcreteCreatorA ..> ConcreteProductA
    ConcreteCreatorB ..> ConcreteProductB
```

### 実装例：ログ出力方法
```mermaid
classDiagram
    class Logger {
        <<interface>>
        +log(message: str): None
    }
    class ConsoleLogger {
        +log(message: str): None
    }
    class FileLogger {
        +log(message: str): None
    }
    class LoggerFactory {
        <<abstract>>
        +create_logger(): Logger
        +get_logger(): Logger
    }
    class ConsoleLoggerFactory {
        +create_logger(): Logger
    }
    class FileLoggerFactory {
        +create_logger(): Logger
    }
    Logger <|.. ConsoleLogger
    Logger <|.. FileLogger
    LoggerFactory <|-- ConsoleLoggerFactory
    LoggerFactory <|-- FileLoggerFactory
    LoggerFactory ..> Logger
    ConsoleLoggerFactory ..> ConsoleLogger
    FileLoggerFactory ..> FileLogger
```

```mermaid
sequenceDiagram
    participant Client
    participant ConcreteLoggerFactory
    participant LoggerFactory
    participant ConcreteLogger
    participant Logger

    Client->>ConcreteLoggerFactory: new
    Client->>LoggerFactory: get_logger()
    activate LoggerFactory
    LoggerFactory->>LoggerFactory: create_logger()
    activate LoggerFactory
    LoggerFactory->>ConcreteLogger: new
    ConcreteLogger-->>LoggerFactory: logger
    deactivate LoggerFactory
    LoggerFactory-->>Client: logger
    deactivate LoggerFactory
    Client->>Logger: log(message)
    activate Logger
    Logger->>ConcreteLogger: log(message)
    activate ConcreteLogger
    ConcreteLogger-->>Logger: 
    deactivate ConcreteLogger
    Logger-->>Client: 
    deactivate Logger
```

```python
from abc import ABC, abstractmethod

class Logger(ABC):
    @abstractmethod
    def log(self, message: str) -> None:
        pass

class ConsoleLogger(Logger):
    def log(self, message: str) -> None:
        print(f"Console: {message}")

class FileLogger(Logger):
    def log(self, message: str) -> None:
        print(f"File: {message}")  # 実際にはファイルに書き込む

class LoggerFactory(ABC):
    @abstractmethod
    def create_logger(self) -> Logger:
        pass

    def get_logger(self) -> Logger:
        logger = self.create_logger()
        return logger

class ConsoleLoggerFactory(LoggerFactory):
    def create_logger(self) -> Logger:
        return ConsoleLogger()

class FileLoggerFactory(LoggerFactory):
    def create_logger(self) -> Logger:
        return FileLogger()

# 使用例
def client_code(factory: LoggerFactory) -> None:
    logger = factory.get_logger()
    logger.log("This is a test message")

if __name__ == "__main__":
    print("Using Console Logger:")
    client_code(ConsoleLoggerFactory())
    
    print("\nUsing File Logger:")
    client_code(FileLoggerFactory())
```

### 実行結果

```
Using Console Logger:
Console: This is a test message

Using File Logger:
File: This is a test message
```

### 解説

- `LoggerFactory`は抽象基底クラスで、具体的なファクトリクラスはこれを継承して実装します。
- `create_logger`メソッドがFactory Methodで、具体的なLoggerオブジェクトの生成を行います。
- このパターンにより、ログ出力方法の追加が容易になり、クライアントコードを変更せずにロギングシステムを拡張できます。

## パターンの比較

| 特徴 | Strategyパターン | Factory Methodパターン |
|------|------------------|------------------------|
| 主な用途 | アルゴリズムの切り替え | オブジェクト生成の抽象化 |
| 焦点 | 振る舞い（メソッド） | オブジェクト生成 |
| 実行時の変更 | 容易 | 通常は固定 |
| クラス構造 | 比較的フラット | 階層的 |
| 新しい実装の追加 | 新しいStrategyクラスを追加 | 新しいCreatorとProductクラスを追加 |
| 主な利点 | アルゴリズムの動的切り替え | オブジェクト生成ロジックの分離 |
| 適用シーン | 複数のアルゴリズムが存在し、実行時に切り替えたい場合 | クラスのインスタンス化を抽象化し、サブクラスで決定したい場合 |

## パターンの組み合わせ

StrategyパターンとFactory Methodパターンは、以下のように組み合わせて使用することができます。

```python
from abc import ABC, abstractmethod

# Strategy
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount: float) -> None:
        pass

class CreditCardPayment(PaymentStrategy):
    def pay(self, amount: float) -> None:
        print(f"クレジットカードで{amount}円支払いました。")

class PayPalPayment(PaymentStrategy):
    def pay(self, amount: float) -> None:
        print(f"PayPalで{amount}円支払いました。")

# Simple Factory
class PaymentStrategyFactory:
    @staticmethod
    def create_payment_strategy(payment_type: str) -> PaymentStrategy:
        if payment_type == "credit_card":
            return CreditCardPayment()
        elif payment_type == "paypal":
            return PayPalPayment()
        else:
            raise ValueError(f"Unknown payment type: {payment_type}")

# Context
class Payment:
    def __init__(self, strategy: PaymentStrategy):
        self.strategy = strategy

    def process_payment(self, amount: float) -> None:
        self.strategy.pay(amount)

    def set_strategy(self, strategy: PaymentStrategy) -> None:
        self.strategy = strategy

# Client code
if __name__ == "__main__":
    payment_types = ["credit_card", "paypal"]
    for payment_type in payment_types:
        strategy = PaymentStrategyFactory.create_payment_strategy(payment_type)
        payment = Payment(strategy)
        payment.process_payment(1000)
```

### 実行結果

```
クレジットカードで1000円支払いました。
PayPalで1000円支払いました。
```

この例では、Simple Factoryパターン（Factory Methodの簡略版）を使用して適切なStrategyを生成し、Strategyパターンで使用しています。これにより、支払い方法の選択と実行を柔軟に行うことができます。

## 他のデザインパターンとの関連

1. **Abstract Factoryパターン**: Factory Methodパターンの拡張で、関連するオブジェクトのファミリーを作成する際に使用されます。

2. **Commandパターン**: Strategyパターンと似ていますが、コマンドのキューイングや履歴管理などの追加機能を提供します。

3. **Decoratorパターン**: 既存のオブジェクトに新しい責任を動的に追加する際に使用され、Strategyパターンと組み合わせて使用されることがあります。

4. **Dependency Injection**: オブジェクトの依存関係を外部から注入するテクニックで、StrategyパターンやFactory Methodパターンと相性が良いです。

## まとめ

- Strategyパターンは、アルゴリズムの動的な切り替えに適しており、振る舞いの変更を容易にします。
- Factory Methodパターンは、オブジェクト生成の抽象化に適しており、オブジェクト生成の柔軟性を高めます。
- これらのパターンを比較することで、それぞれの特徴と適用場面をより深く理解できます。
- パターンを組み合わせることで、より柔軟で拡張性の高いシステムを構築できます。
- デザインパターンの選択は、問題の性質や設計の目標に応じて行うべきです。

StrategyパターンとFactory Methodパターンの特徴、関連性、そして他のパターンとの関係を理解することで、より適切なパターンの選択と応用が可能になります。実際の開発では、これらのパターンを単独で使用するだけでなく、状況に応じて組み合わせたり他のパターンと併用したりすることで、より強力で柔軟な設計ソリューションを実現できます。