Factory Method

パターンの目的

p.116,line.3-5: オブジェクトを作成するときのインターフェイスだけを規定して、実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。 FactoryMethodパターンは、インスタンス化をサブクラスに任せる。

GoF パターンのクラス図

structure-08-05:factorymethod

MixJuice 版 Factory Method (改善)

解決される GoF パターンの問題点

情報隠蔽の問題点

p.118,line.17-18: factory methodが2つのクラス階層の間の関係をどのように定義しているかに注意してほしい。それはクラスの組み合わせに関する知識を局所化している。

補足: Factory Method によってクラスの組み合わせに関する知識を局所化しているのに、既存のプログラミング言語では、ソースコード上で必ずしも局所的に表現できない点が問題である。

対策

対応する ConcreteProduct と ConcreteCreator を同一モジュール内で定義する。

結果

ConcreteProduct と ConcreteCreate のペアごとに同一モジュール内で定義することにより、モジュール単位での開発・保守を容易にする。

サンプルコード

structure-08-09:factorymethod_sample

module framework {
  define abstract class Creator {
    define abstract Product createProduct();
    ...
  }
  define abstract class Product {...}
}
module productA extends framework {
  define class CreatorA extends Creator {
    Product createProduct() { return new ProductA(); }
    ...
  }
  define class ProductA extends Product {
    define ProductA(){...}
    ...
  }
}
module productB extends framework {
  define class CreatorB extends Creator {
    Product createProduct() { return new ProductB(); }
    ...
  }
  define class ProductB extends Product {
    define ProductB(){...}
    ...
  }
}

MixJuice 版 Factory Method (別解)

GoF パターンの問題点

型安全性の問題点
Factory Method パターンではオブジェクトを生成するメソッドをスーパークラスで宣言し、サブクラスで override することにより実際に生成するオブジェクトのクラスをカスタマイズする。ここで、このメソッドの型はスーパークラスで宣言した時点で決まるため、実際に生成するオブジェクトのクラスはメソッドの型には現れない。この結果、実際に生成するオブジェクト特有のメソッドを呼び出す必要がある場合にはダウンキャストが必要である。
クラス数増加の問題点

p.121, line 17-19:

  1. テンプレートを用いてサブクラス化を避ける。

先に述べたように、factory method の潜在的な問題点として、適切な ConcreteProduct オブジェクトを生成するためにサブクラスを作成しなければならない点をあげることができる。

カスタマイズするためにはサブクラスを作らなければならず、クラス数が増加する。

対策

ConcreteCreator を Creator のサブクラスとして実装するのではなく、差分として実装する。同様に ConcreteProduct を Product に対する差分として実装する。

factory method が生成したオブジェクトをインスタンス変数に保存する場合、 Creator は ConcreteProduct を知らないので、 Product 型の変数に代入することになる。しかし、ConcreteCreator で ConcreteProduct が必要な場合には、 (downcast を使わないためには)別のインスタンス変数が必要になる。同じオブジェクトを指すのにこれは無駄、また一貫性管理の必要性を生む。 MixJuice では、Product への差分として ConcreteProduct を実現すればひとつの変数で済む。

結果

構造

structure-08-09:factorymethod

module m {
  define class Product {
    define abstract Product();
  }
  define class Creator {
    define Product product;
    define void anOperation() { ... product = new Product(); ... }
  }
}

module m.implementation extends m {
  class Product {
    Product() {...}
    define void concreteMethod() {...}
  }
  class Creator {
    define void anotherOperation() {
      ...
      product.concreteMethod(); // キャスト不要
      ...
    }
  }
}

サンプルコード


MixJuice によるデザインパターン改善カタログ


田中 哲 <akr@m17n.org>, 一杉 裕志 <y-ichisugi@aist.go.jp>