Abstract Factory パターンを世界の料理で理解する

## はじめに

プログラミングのデザインパターンは、実際の生活の例を用いると非常に理解しやすくなります。今回は、Abstract Factoryパターンを、世界各国の料理を例にPythonで実装して説明します。この例を通じて、Abstract Factoryパターンの柔軟性と拡張性を理解しましょう。

## Abstract Factoryパターンとは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/36125178-ad6f-da9a-59d6-f9e8c9adcc23.png)

Abstract Factoryパターンは、関連する一連のオブジェクト（この場合は料理）を、その具体的な種類を指定せずに生成するためのパターンです。簡単に言えば、「料理を作る方法」を定義しておいて、実際にどの国の料理を作るかは後で決められるようにする、というものです。

### なぜAbstract Factoryパターンを使うのか

1. **柔軟性**: 様々な国の料理を、同じ手順で作れるようになります。
2. **一貫性**: 関連する料理（前菜、主菜、デザートなど）を一緒に作ることで、一つの国の料理としての一貫性を保てます。
3. **拡張性**: 新しい国の料理を追加するのが簡単になります。

## Pythonによる実装例の解説

```mermaid
classDiagram
    class Appetizer {
        <<abstract>>
        +prepare()*
    }
    class MainCourse {
        <<abstract>>
        +prepare()*
    }
    class Dessert {
        <<abstract>>
        +prepare()*
    }
    class CuisineFactory {
        <<abstract>>
        +create_appetizer()*
        +create_main_course()*
        +create_dessert()*
    }
    class JapaneseAppetizer {
        +prepare()
    }
    class JapaneseMainCourse {
        +prepare()
    }
    class JapaneseDessert {
        +prepare()
    }
    class ItalianAppetizer {
        +prepare()
    }
    class ItalianMainCourse {
        +prepare()
    }
    class ItalianDessert {
        +prepare()
    }
    class IndianAppetizer {
        +prepare()
    }
    class IndianMainCourse {
        +prepare()
    }
    class IndianDessert {
        +prepare()
    }
    class JapaneseCuisineFactory {
        +create_appetizer()
        +create_main_course()
        +create_dessert()
    }
    class ItalianCuisineFactory {
        +create_appetizer()
        +create_main_course()
        +create_dessert()
    }
    class IndianCuisineFactory {
        +create_appetizer()
        +create_main_course()
        +create_dessert()
    }

    Appetizer <|-- JapaneseAppetizer
    Appetizer <|-- ItalianAppetizer
    Appetizer <|-- IndianAppetizer
    MainCourse <|-- JapaneseMainCourse
    MainCourse <|-- ItalianMainCourse
    MainCourse <|-- IndianMainCourse
    Dessert <|-- JapaneseDessert
    Dessert <|-- ItalianDessert
    Dessert <|-- IndianDessert
    CuisineFactory <|-- JapaneseCuisineFactory
    CuisineFactory <|-- ItalianCuisineFactory
    CuisineFactory <|-- IndianCuisineFactory
    JapaneseCuisineFactory ..> JapaneseAppetizer : creates
    JapaneseCuisineFactory ..> JapaneseMainCourse : creates
    JapaneseCuisineFactory ..> JapaneseDessert : creates
    ItalianCuisineFactory ..> ItalianAppetizer : creates
    ItalianCuisineFactory ..> ItalianMainCourse : creates
    ItalianCuisineFactory ..> ItalianDessert : creates
    IndianCuisineFactory ..> IndianAppetizer : creates
    IndianCuisineFactory ..> IndianMainCourse : creates
    IndianCuisineFactory ..> IndianDessert : creates
```

```mermaid
sequenceDiagram
    participant Client
    participant CuisineFactory
    participant Appetizer
    participant MainCourse
    participant Dessert

    Client->>+CuisineFactory: create_appetizer()
    CuisineFactory->>+Appetizer: new Appetizer()
    Appetizer-->>-CuisineFactory: appetizer
    CuisineFactory-->>-Client: appetizer

    Client->>+Appetizer: prepare()
    Appetizer-->>-Client: prepared appetizer

    Client->>+CuisineFactory: create_main_course()
    CuisineFactory->>+MainCourse: new MainCourse()
    MainCourse-->>-CuisineFactory: main course
    CuisineFactory-->>-Client: main course

    Client->>+MainCourse: prepare()
    MainCourse-->>-Client: prepared main course

    Client->>+CuisineFactory: create_dessert()
    CuisineFactory->>+Dessert: new Dessert()
    Dessert-->>-CuisineFactory: dessert
    CuisineFactory-->>-Client: dessert

    Client->>+Dessert: prepare()
    Dessert-->>-Client: prepared dessert

    Note over Client,Dessert: 完全な料理が準備される
```

### 抽象クラスの定義

まず、料理の構成要素とファクトリーの抽象クラスを定義します。

```python
from abc import ABC, abstractmethod

class Appetizer(ABC):
    @abstractmethod
    def prepare(self):
        pass

class MainCourse(ABC):
    @abstractmethod
    def prepare(self):
        pass

class Dessert(ABC):
    @abstractmethod
    def prepare(self):
        pass

class CuisineFactory(ABC):
    @abstractmethod
    def create_appetizer(self):
        pass

    @abstractmethod
    def create_main_course(self):
        pass

    @abstractmethod
    def create_dessert(self):
        pass
```

これらの抽象クラスは、それぞれの国の料理が実装すべきメソッドを定義しています。

### 具体的な実装

次に、日本、イタリア、インドの料理それぞれの具体的な実装を行います。

```python
# 日本料理の具体クラス
class JapaneseAppetizer(Appetizer):
    def prepare(self):
        return "お刺身を盛り付けました"

# ... 他の日本料理クラスも同様に実装 ...

class JapaneseCuisineFactory(CuisineFactory):
    def create_appetizer(self):
        return JapaneseAppetizer()

    # ... 他のメソッドも同様に実装 ...

# イタリア料理の具体クラス
class ItalianAppetizer(Appetizer):
    def prepare(self):
        return "ブルスケッタを作りました"

# ... 他のイタリア料理クラスも同様に実装 ...

# インド料理の具体クラス
class IndianAppetizer(Appetizer):
    def prepare(self):
        return "サモサを揚げました"

# ... 他のインド料理クラスも同様に実装 ...
```

各国の料理に対して、具体的な料理の内容とそれらを生成するファクトリークラスを実装しています。

### クライアント関数

最後に、これらのファクトリーを使用して実際に料理を準備する関数を定義します。

```python
def prepare_meal(factory):
    appetizer = factory.create_appetizer()
    main_course = factory.create_main_course()
    dessert = factory.create_dessert()

    print("食事の準備:")
    print(appetizer.prepare())
    print(main_course.prepare())
    print(dessert.prepare())
```

この関数は、どの国の料理ファクトリーが渡されても同じように動作します。これがAbstract Factoryパターンの真髄です。

### 実行例

```python
if __name__ == "__main__":
    print("日本料理:")
    japanese_factory = JapaneseCuisineFactory()
    prepare_meal(japanese_factory)

    print("\nイタリア料理:")
    italian_factory = ItalianCuisineFactory()
    prepare_meal(italian_factory)

    print("\nインド料理:")
    indian_factory = IndianCuisineFactory()
    prepare_meal(indian_factory)
```

この部分で実際に各国の料理の準備を行います。出力は以下のようになります：

```
日本料理:
食事の準備:
お刺身を盛り付けました
天ぷらを揚げました
抹茶アイスを用意しました

イタリア料理:
食事の準備:
ブルスケッタを作りました
パスタを茹でました
ティラミスを用意しました

インド料理:
食事の準備:
サモサを揚げました
カレーを煮込みました
ラッシーを作りました
```

## Abstract Factoryパターンの利点

1. **柔軟性**: 新しい国の料理を追加する際、既存のコードを変更せずに新しいファクトリーとクラスを追加するだけで済みます。
2. **一貫性**: 各国の料理セットの構成要素が適切に組み合わされます。例えば、日本のデザートがイタリアの前菜と組み合わさることはありません。
3. **カプセル化**: 具体的な料理の作り方の詳細は、各クラス内に隠蔽されています。

## 実際の応用例

Abstract Factoryパターンは、以下のような状況で特に有用です：

1. クロスプラットフォームのUIライブラリ：Windows、Mac、Linuxなど、異なるOSに対応するUIコンポーネントを作成する際に使用できます。
2. データベース接続ライブラリ：MySQL、PostgreSQL、SQLiteなど、異なるデータベースシステムに対応するコネクションやクエリビルダーを作成する際に活用できます。
3. ゲーム開発：異なる難易度レベルや異なるテーマの敵キャラクター、アイテムなどを生成する際に使用できます。

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/8572cae4-3085-bcd3-cd92-f1cdb22ba100.png)


Abstract Factoryパターンを世界の料理の例で実装することで、このパターンの柔軟性と拡張性がより明確になりました。このパターンを使用することで、新しい種類のオブジェクト（この場合は新しい国の料理）を追加する際に、既存のコードを変更せずに済むという大きな利点があります。

プログラミングの世界でも、適切な「レシピ」（デザインパターン）を選ぶことで、より柔軟で保守しやすいシステムを作ることができます。次回あなたがプログラムを書く際、この世界の料理の例を思い出してAbstract Factoryパターンの適用を検討してみてください。

## 実装(全部)

```python
from abc import ABC, abstractmethod

# 抽象クラス
class Appetizer(ABC):
    @abstractmethod
    def prepare(self):
        pass

class MainCourse(ABC):
    @abstractmethod
    def prepare(self):
        pass

class Dessert(ABC):
    @abstractmethod
    def prepare(self):
        pass

class CuisineFactory(ABC):
    @abstractmethod
    def create_appetizer(self):
        pass

    @abstractmethod
    def create_main_course(self):
        pass

    @abstractmethod
    def create_dessert(self):
        pass

# 日本料理の具体クラス
class JapaneseAppetizer(Appetizer):
    def prepare(self):
        return "お刺身を盛り付けました"

class JapaneseMainCourse(MainCourse):
    def prepare(self):
        return "天ぷらを揚げました"

class JapaneseDessert(Dessert):
    def prepare(self):
        return "抹茶アイスを用意しました"

class JapaneseCuisineFactory(CuisineFactory):
    def create_appetizer(self):
        return JapaneseAppetizer()

    def create_main_course(self):
        return JapaneseMainCourse()

    def create_dessert(self):
        return JapaneseDessert()

# イタリア料理の具体クラス
class ItalianAppetizer(Appetizer):
    def prepare(self):
        return "ブルスケッタを作りました"

class ItalianMainCourse(MainCourse):
    def prepare(self):
        return "パスタを茹でました"

class ItalianDessert(Dessert):
    def prepare(self):
        return "ティラミスを用意しました"

class ItalianCuisineFactory(CuisineFactory):
    def create_appetizer(self):
        return ItalianAppetizer()

    def create_main_course(self):
        return ItalianMainCourse()

    def create_dessert(self):
        return ItalianDessert()

# インド料理の具体クラス
class IndianAppetizer(Appetizer):
    def prepare(self):
        return "サモサを揚げました"

class IndianMainCourse(MainCourse):
    def prepare(self):
        return "カレーを煮込みました"

class IndianDessert(Dessert):
    def prepare(self):
        return "ラッシーを作りました"

class IndianCuisineFactory(CuisineFactory):
    def create_appetizer(self):
        return IndianAppetizer()

    def create_main_course(self):
        return IndianMainCourse()

    def create_dessert(self):
        return IndianDessert()

# クライアント関数
def prepare_meal(factory):
    appetizer = factory.create_appetizer()
    main_course = factory.create_main_course()
    dessert = factory.create_dessert()

    print("食事の準備:")
    print(appetizer.prepare())
    print(main_course.prepare())
    print(dessert.prepare())

# メイン処理
if __name__ == "__main__":
    print("日本料理:")
    japanese_factory = JapaneseCuisineFactory()
    prepare_meal(japanese_factory)

    print("\nイタリア料理:")
    italian_factory = ItalianCuisineFactory()
    prepare_meal(italian_factory)

    print("\nインド料理:")
    indian_factory = IndianCuisineFactory()
    prepare_meal(indian_factory)
```