<一覧に戻る

ファクトリメソッドパターン

このセクションでは、デザインパターンの中でも「オブジェクトの作り方」にフォーカスしたファクトリメソッドパターンを学びます。

クラス名を直接書いてインスタンス化していると、新しい種類を追加するたびにコードを直す羽目になっていませんか?

そんな小さな不便が積み重なると、保守がつらくなります。 そこで役に立つのが、オブジェクト生成のやり方を整理するファクトリメソッドパターンです。

なぜ必要?直感的なやり方の限界

ここでは、あえて“よくある書き方”からスタートして課題を体感してみます。 次のように、必要な場所で直接クラスを呼び出すやり方は、最初はとてもシンプルです。

class Sedan:
    def drive(self):
        return "Driving a sedan!"

class SUV:
    def drive(self):
        return "Driving an SUV!"


car = Sedan()  # ここで具体クラスを直接指定
print(car.drive())

一見簡単ですが、もし「トラック」を追加したくなったらどうでしょう?クライアント側のあちこちに書かれた Sedan()SUV() を探して置き換える必要が出てきます。

これが数箇所ならまだしも、規模が大きくなるほど手戻りが増え、テストのコストも上がってしまいます。

ファクトリメソッドパターンの目的を言い換える

ここからは、本題の目的を初心者にもわかりやすい言葉で整理します。 抽象的な言い回しより「何ができるか」に目を向けると理解しやすくなります。

  • オブジェクトの生成をカプセル化することで、クライアントは「何を作るか」をリクエストするだけで済みます。つまり、具体的なクラス名を知らなくても使えます。
  • 新しい種類を増やす場合、できる限り「追加のみ」で対応できます。既存コードを書き換える量を減らせるので、保守性が上がります。
  • テストでは、モックやスタブを工場(ファクトリ)に差し込みやすくなります。これにより動作確認がやりやすくなります。

具体的な実践例で見ていきましょう。

実装例1:まずは「シンプルファクトリ」から入って理解を深める

いきなり“本来の”ファクトリメソッドパターンに入るより、初心者の方は「1つのファクトリクラスが条件によって適切なクラスを返す」形(いわゆるシンプルファクトリ)から掴むと理解がスムーズです。

ここでは車を例にします。

from abc import ABC, abstractmethod
from typing import Type, Dict

# 1) 共通インターフェース
class Car(ABC):
    @abstractmethod
    def drive(self) -> str:
        ...

# 2) 具体クラス
class Sedan(Car):
    def drive(self) -> str:
        return "Driving a sedan!"

class SUV(Car):
    def drive(self) -> str:
        return "Driving an SUV!"

# 3) ファクトリ(シンプルな実装)
class CarFactory:
    def __init__(self) -> None:
        # 車種名とクラスの対応表
        self._registry: Dict[str, Type[Car]] = {
            "sedan": Sedan,
            "suv": SUV,
        }

    def create_car(self, car_type: str) -> Car:
        try:
            cls = self._registry[car_type.lower()]
        except KeyError:
            raise ValueError(f"Unknown car type: {car_type}")
        return cls()

# 4) 使う側(クライアント)
factory = CarFactory()

car1 = factory.create_car("sedan")
print(car1.drive())  # -> Driving a sedan!

car2 = factory.create_car("suv")
print(car2.drive())  # -> Driving an SUV!

ここで大事なのは「使う側は具体的なクラス名(SedanやSUV)を知らなくてもよい」という点です。CarFactoryに「sedan」や「suv」といったラベルを渡すだけで、適切なインスタンスが返ってきます。新しい種類を追加したいときは、クラスを1つ作ってレジストリ(対応表)に1行足せばOKです。if文がズラリと並ぶコードより、追加・変更が分かりやすくなります。

このコードでは、Carという抽象クラスが「車ならdriveを持っているべき」という約束を表しています。SedanやSUVはその約束を守って具体的な動きを提供します。ファクトリは「どのクラスを使うか」という悩みを引き受け、クライアントは「何が欲しいか」だけを伝えます。これにより、クライアントのコードから具体クラス名が消え、読みやすさとテストのしやすさが上がります。

実装例2:“本来の”ファクトリメソッドに一歩踏み込む

次は、GoF(Gang of Four)で定義される「ファクトリメソッドパターン」に寄せた形を見てみましょう。ポイントは「生成のためのメソッドを持つ基底クラスがあり、そのメソッドをサブクラスがオーバーライドして、どの具体クラスを作るかを決める」ことです。

from abc import ABC, abstractmethod

# Product
class Car(ABC):
    @abstractmethod
    def drive(self) -> str:
        ...

class Sedan(Car):
    def drive(self) -> str:
        return "Driving a sedan!"

class SUV(Car):
    def drive(self) -> str:
        return "Driving an SUV!"

# Creator(工場のひな型)
class CarStore(ABC):
    # ファクトリメソッド:サブクラスが「何を作るか」を決める
    @abstractmethod
    def create_car(self) -> Car:
        ...

    # 共通の手順を用意して、生成だけサブクラスに任せる
    def order_car(self) -> str:
        car = self.create_car()
        return car.drive()

# ConcreteCreator(工場の具体実装)
class SedanStore(CarStore):
    def create_car(self) -> Car:
        return Sedan()

class SUVStore(CarStore):
    def create_car(self) -> Car:
        return SUV()


sedan_store = SedanStore()
print(sedan_store.order_car())  # -> Driving a sedan!

suv_store = SUVStore()
print(suv_store.order_car())    # -> Driving an SUV!

ここでは、CarStoreに「車を注文する」という共通の流れをまとめ、実際にどの車を作るか(create_car)はサブクラス(SedanStoreやSUVStore)に委ねています。クライアントは「どのストア(Creator)に注文するか」を選ぶだけで、具体クラスの生成はストア側に隠されます。これが「生成の委譲」による柔軟性です。

💡 豆知識:GoFとは? GoF(Gang of Four)とは、ソフトウェア設計の世界的な名著 『Design Patterns: Elements of Reusable Object-Oriented Software(デザインパターン ― 再利用のためのオブジェクト指向設計)』 の著者4人を指します。この4人の名前の頭文字をとって「Gang of Four(四人組)」=GoFと呼ばれています。

Creator(Store)とは?

ファクトリメソッドパターンにおけるCreator(クリエイター)とは、オブジェクト生成の流れを定義する抽象クラスのことです。

実際に「どのクラスを作るか」は知らず、生成の手順だけを決めます。たとえば CarStore は、order_car() で「車を注文して返す」という共通の流れを持ちながら、実際に作る車(SedanSUV)はサブクラスに任せています。
このように、Creatorは「生成の仕組み」を定義し、サブクラス(ConcreteCreator)が「何を作るか」を実装します。

「Store(ストア)」とは、Creatorを“お店”のように見立てたものです。クライアントは「どんな車が欲しいか」を注文するだけで、具体的なクラスを意識せずに使えます。 この設計により、生成手順の共通化と拡張性を両立し、新しい種類を追加しても既存コードをほとんど修正せずに済む柔軟な構造が実現できます。

どちらを選ぶ?シンプルファクトリとファクトリメソッドの違い

ここまで読んで「どっちを使えばいいの?」と感じた方も多いはず。使い分けの目線を簡潔にまとめます。

  • まずはシンプルファクトリで十分なことが多いです。1つの場所に生成ロジックを集めて、分岐を管理しやすくします。小さなプロジェクトや、種類が少ない場面ではこれが実用的です。
  • 生成と利用の手順をしっかり分けたい、生成のバリエーションごとに手順も変わる、といった場合はファクトリメソッドがフィットします。Creator(Store)の共通手順に乗せつつ、作る対象だけサブクラスに任せられます。

どちらも「クライアントから具体クラス名を隠す」というゴールは同じです。プロジェクトの規模や変更の頻度に応じて選びましょう。

新しい種類を追加するには?

ここでは、先ほどのシンプルファクトリ版に「トラック」を足すケースを想像してみます。変更は驚くほど小さくできます。

  • 新しいクラス Truck を Car を継承して作る
  • ファクトリのレジストリに "truck": Truck を1行追加する

これだけで、クライアント側は "truck" を指定するだけで新しい車を使えるようになります。もしあなたが「追加するたびに既存コードを触るのが怖い」と感じているなら、ファクトリを導入する価値は大きいはずです。

まとめ:小さく始めて、必要に応じて進化させる

ファクトリメソッドパターンは、「生成を任せる」というたった1つの工夫でコードの保守性と拡張性を大きく高めます。最初はシンプルファクトリから始めてもOKです。必要に応じてCreator/ConcreteCreatorの構成に発展させれば、共通手順と生成の責務分離がさらに進みます。

  • 具体クラス名をクライアントから追い出すことで、変更に強くなる
  • 新しい種類の追加が「追加のみ」で済む設計に近づける
  • テストで差し替えやすく、品質を保ちやすい

「新しい機能を足すたびに怖い」と感じるコードが、ほんの少しの設計工夫で落ち着きを取り戻します。まずは小さな場面で試して、理解を深めていきましょう。

出力結果: