勉強日記

毎日投稿

GoF本 FactoryMethod

まとめ

  • オブジェクトを生成・返却するメソッドのインタフェースを定義する
  • 実際にどのクラスを生成するかはサブクラスの実装|overrideに委ねる

別名

Virtual Constructor

モチベーション

  • 世の中のプログラムフレームワークは抽象クラスを扱う
  • フレームワークはどんな具象クラスが渡されるかわからないので、抽象クラス間の関係を管理する
  • フレームワークは、管理対象のクラスのオブジェクトの生成を担うこともある
    • でも抽象クラスだから直接はインスタンシエートできない
  • フレームワークは「いつ」オブジェクトを生成すべきかを知っている
  • 「どの種類の」クラスのオブジェクトを生成すべきかはわからない → 分離してクライアントコードに委ねる

つかいどころ

  • Creatorは生成対象の具体的なクラスを知らない
  • ので、派生ConcreteCreatorに生成対象のクラスを特定してもらう
  • Strategyパターンのように、生成対象が仕事を別のクラス(helper subclasses)に委譲している場合、どのhelper subclassと組み合わせて使うかの知識を一ヶ所にまとめたい

登場人物

  • Product
    • 生成するオブジェクトのインタフェースを定義
  • ConcreteProduct
    • Productの派生
    • Productに汎化できる程度には似通っている必要がある
      • BuilderパターンにおけるProductは汎化しない。複雑で共通項が少なすぎるから
  • Creator
    • ファクトリメソッドを定義する
      • CreateProduct() : Product とかそんなかんじ
        • CreateAndSetupProduct() : Product
          の中で呼び出したりする(TemplateMethodパターン)
  • ConcreteCreator
    • Creatorの派生
    • ファクトリメソッドを実装する

功罪

  • Product派生のいかなるConcreteProductクラスでもインスタンシエートできる
  • ConcreteProductの数だけConcreteCreatorクラスを定義しないといけない
  • オブジェクト生成処理の前後等にhookをもうけることができる
    • デフォルトなにもしない
    • 生成したらダイアログを出すようoverrideする、とかできる
  • 並列な継承木を結びつける
    • BridgeパターンのImplementor、StrategyパターンのStrategyなど
    • ConcreteProductAに対してはStrategy1を使います」といった知識をCreator内にカプセル化できる

実装

2種類に大別できる

  1. Creatorクラスは抽象クラス、ファクトリメソッドは抽象メソッド
    • 基本のき
  2. Creatorクラスは具象クラス、ファクトリメソッドは実装をもつ
    • たとえば、Productは具象クラスで、Strategyに処理を委譲している場合
    • CreatorのファクトリメソッドではProductの生成と、デフォルトのNullStrategyの設定のみ行う
    • ConcreteCreatorのファクトリメソッドのoverrideの中では、基底のファクトリメソッドを呼び出し、StrategyをConcreteStrategyにすげ替える

ConcreteCreator増えすぎ問題に対処するバリアント

Creatorを具象クラスにして、下記を適用する

  • ファクトリメソッドに引数を追加
    どのProductを作るかをenumとかで指定する
  • 作るべきProductクラスをCreatorのメンバ変数に格納する
    • クラスが第一級オブジェクトである言語限定
      • JSとか
  • ジェネリクス使う
    • C++のtemplateとか
      • この場合、ソースコードが簡潔になるだけで、オブジェクトコードはCreator<T>クラスの具象化の数だけ増えるので注意

用例

あらゆるフレームワーク

関連するパターン

  • AbstractFactoryパターン
    • FactoryMethodパターンで実装されることがある
    • 他の実装としては、Prototypeパターン等
  • TemplateMethodパターン
    • 「基底では中身のないファクトリメソッドを呼び出し、派生でファクトリメソッドの実装を書く」というのは、TemplateMethodそのもの