ソフトウェア設計

ファクトリーメソッドと抽象ファクトリーの違い

GoFのファクトリーメソッドは1つのオブジェクトの具象型の決定をサブクラスに委ね、抽象ファクトリーは関連するオブジェクト群をまとめて生成します。2つの意図と選択基準をSwiftの例で比較します。

読了 4 分
ファクトリーメソッドと抽象ファクトリーの違いのカバー画像

デザインパターンを学んでいると、誰もが一度はつまずくポイントがあります。

それがファクトリーメソッドと抽象ファクトリーです。名前が似ていて、どちらも「オブジェクトを代わりに作る」ため、混同しやすいのです。

面接でも定番の質問なので、この記事でまとめて整理しました。

要点から説明します。ファクトリーメソッドは、オブジェクトをどのクラスとして作るかをサブクラスに任せるパターンです。抽象ファクトリーは、互いに関連する複数のオブジェクト(製品群)をまとめて作ります。

static func makeのように生成処理に名前を付ける静的ファクトリーは、GoFのファクトリーメソッドとは別物です。実務でのAPI設計を知りたいなら、まずSwiftの静的ファクトリーメソッドと区別しておくとよいでしょう。

1つか、1セットか。これが最大の違いです。

ファクトリーメソッドとは?

ファクトリーメソッドは、オブジェクト生成の責任を上位クラスではなくサブクラスに渡すパターンです。

上位クラスは「ボタンを作る」ことだけを定義し、どのボタンを作るかは子クラスが決めます。

コードを見ると、すぐに理解できます。

protocol Button { func render() }

class Dialog {
    // ファクトリーメソッド:サブクラスが何を作るかを決める
    func createButton() -> Button { fatalError("サブクラスで実装") }

    func render() {
        let button = createButton()
        button.render()
    }
}

class IOSDialog: Dialog {
    override func createButton() -> Button { IOSButton() }
}

Dialogは、自分がどのボタンを使うかを知る必要がありません。新しいプラットフォームが登場しても、サブクラスを1つ追加するだけです。

ポイントは継承です。サブクラス化によって生成箇所を差し替える構造になっています。


抽象ファクトリーは何が違う?

抽象ファクトリーは、関連するオブジェクトを1セットとして生成するパターンです。

ボタン1つではなく、ボタン、チェックボックス、スクロールバーのように一緒に扱うオブジェクトをまとめて担当します。

protocol GUIFactory {
    func createButton() -> Button
    func createCheckbox() -> Checkbox
}

class IOSFactory: GUIFactory {
    func createButton() -> Button { IOSButton() }
    func createCheckbox() -> Checkbox { IOSCheckbox() }
}

class MacFactory: GUIFactory {
    func createButton() -> Button { MacButton() }
    func createCheckbox() -> Checkbox { MacCheckbox() }
}

重要なのは一貫性です。iOSファクトリーを使えば、ボタンもチェックボックスもすべてiOSスタイルになります。iOSのボタンにMacのチェックボックスが混ざる事故も防げます。

抽象ファクトリーは通常、ファクトリーオブジェクトを外部から注入して使います。継承ではなく、コンポジションで動作するわけです。

iOSでもMacでも、ファクトリーを差し替えるだけでセット全体が変わります
iOSでもMacでも、ファクトリーを差し替えるだけでセット全体が変わります

ファクトリーメソッドは「何を作るか」を継承で解決し、

抽象ファクトリーは「どのセットを作るか」をコンポジションで解決します。


違いを一覧で比較

言葉だけでは混乱するので、表にまとめました。

項目 ファクトリーメソッド 抽象ファクトリー
目的 1つのオブジェクト生成を委譲 関連するオブジェクト群を生成
基盤 継承(サブクラス化) コンポジション(構成)
作るもの 1種類の製品 複数の製品(製品群)
拡張方法 新しいサブクラスを追加 新しいファクトリーを追加
よくある例 createButton() GUIFactory全体

興味深いのは、抽象ファクトリーの内部が実際にはファクトリーメソッドで構成されることが多い点です。両者は競合関係というより、規模の異なるパターンと考えるとよいでしょう。


いつ使い、いつ避けるべきか

実務での判断基準は、意外と単純です。

  • 作るオブジェクトが1種類だけなら、ファクトリーメソッドで十分です。わざわざファクトリーインターフェースまで作ると過剰設計になります。
  • 一緒に動くオブジェクトが2種類以上あるなら、抽象ファクトリーを検討してください。
  • 製品群が今後も増えなさそうなら、単純な条件分岐のほうがよい場合も多いです。
状況 判断
生成する製品が1つ ファクトリーメソッド
製品がセットで動く 抽象ファクトリー
製品群が頻繁に増える 抽象ファクトリー
分岐が2~3個で済む パターンなしの単純な分岐

パターンは目的ではなく道具です。増える見込みのないコードに先回りして抽象化を入れると、読みづらくなるだけです。

パターンは道具にすぎないので、必要になってから使っても遅くありません
パターンは道具にすぎないので、必要になってから使っても遅くありません

面接ではこう質問されます

Q. ファクトリーメソッドと抽象ファクトリーの違いを説明してください。 A. ファクトリーメソッドは、1つのオブジェクトの生成をサブクラスに委譲する継承ベースのパターンです。抽象ファクトリーは、関連するオブジェクト群を一貫して生成するコンポジションベースのパターンです。作る対象が1つか1セットかが核心となる違いです。

Q. 抽象ファクトリーの中でファクトリーメソッドは使われますか? A. はい、よく使われます。抽象ファクトリーの各生成メソッドが、内部ではファクトリーメソッドとして実装されることが多いのです。両者は対立する概念ではなく、扱う範囲が異なるパターンです。

これで、2つのパターンは名前を見るだけで「1つか、セットか」と思い浮かべられれば十分です。面接前にこの記事を一度読み返せば、きっと安心できます。