Abstract Factory

パターンの目的

p.95, line.3-4: 互いに関連したり依存し合うオブジェクト群を、その具象クラスを明確にせずに生成するためのインタフェースを提供する。

GoF パターンのクラス図

structure-08-05:abstractfactory

MixJuice 版 Abstract Factory (改善)

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

拡張性の問題点

p.98, line.10-15: 4.新たな種類の部品に対応することが困難である。新たな種類の部品に対応できるようにAbstractFactoryパターンを拡張することは容易ではない。なぜならば、AbstractFactoryクラスのインターフェイスは生成される部品の集合を固定しているからである。新たな種類の部品への対応は、インターフェイスの修正が必要となるために、AbstractFactoryクラスだけでなくそのすべてのサブクラスを修正しなければならなくなる。[実装]の節で、この問題に対する1つの解決策について議論する。

補足:最後に触れられている「1つの解決策」とは、実装の節の3項目めに書いてある。オブジェクトを生成するオペレーションに生成すべきクラスを指定するパラメータを付加するというもの。この方法を使うと返値のダウンキャストが必要になる。

対策

AbstractFactory クラスに対するアブストラクトメソッド追加で対処可能である。

結果

既存のソースコードを修正しなくても、新たな種類の部品の生成に対応することが可能になる。しかし、既存のすべての ConcreteFactory に対し追加された method を実装する補完モジュールが必要となる。

構造

structure-08-09:abstractfactory1

module original {
  define abstract class AbstractFactory {
    define abstract AbstractProductA createProductA();
    define abstract AbstractProductB createProductB();
  }
  define class ConcreteFactory1 extends AbstractFactory {
    AbstractProductA createProductA(){...}
    AbstractProductB createProductB(){...}
  }
  define class ConcreteFactory2 extends AbstractFactory {
    AbstractProductA createProductA(){...}
    AbstractProductB createProductB(){...}
  }

  define abstract class AbstractProductA {...}
  define class ProductA1 extends AbstractProductA {...}
  define class ProductA2 extends AbstractProductA {...}

  define abstract class AbstractProductB {...}
  define class ProductB1 extends AbstractProductB {...}
  define class ProductB2 extends AbstractProductB {...}

  define class Client {
    AbstractFactory factory;
    define void m1(){
      AbstractProductA a = factory.createProductA();
      AbstractProductB b = factory.createProductB();
    }
  }
}
module extension extends original {
  class AbstractFactory {
    define abstract AbstractProductC createProductC();
  }
  class ConcreteFactory1 {
    AbstractProductC createProductC(){...}
  }
  class ConcreteFactory2 {
    AbstractProductC createProductC(){...}
  }

  define abstract class AbstractProductC {...}
  define class ProductC1 extends AbstractProductC {...}
  define class ProductC2 extends AbstractProductC {...}

  class Client {
    define void m2(){
      AbstractProductC c = factory.createProductC();
    }
  }
}

MixJuice 版 Abstract Factory (別解)

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

拡張性の問題点

p.98 「4. 新たな種類の部品に対応することが困難である。」

新たな種類の部品に対応するためには、AbstractFactory クラスの修正が必要になる。

この問題に対処するために、引数で種類を指定するという方法が述べられているが、この方法は静的型にそぐわないという問題がある。

型安全性の問題点

p.100, line.12-18: すなわち、すべての部品が、戻り値の型により与えられる同一の抽象化されたインタフェースを備えた形でクライアントに返される。したがって、クライアントは部品のクラスに関して区別をしたり、安全な仮定をおくことが不可能になる。もし、クライアントがサブクラスに特有のオペレーションを実行したくても、抽象クラスのインタフェースを通してでは、それらを実行することはできない。クライアントはダウンキャストを(たとえば C++ の dynamic_cast を使って)行なうことができるかも知れないが、これは常に実行可能、または安全であるとは限らない。なぜならば、ダウンキャストは失敗する可能性があるからである。

生成された対象の内容をアクセスするためにダウンキャストが必要になりがちである。 GoF のウインドウシステムの例でいえば、ウインドウにピックスマップを背景として張り付ける場合、ウインドウオブジェクトはピックスマップオブジェクトのシステム依存な情報を得るためにダウンキャストを必要とする。

クラス数増加の問題点
生成される対象(Product)に加え、生成時だけに使われるクラス(Factory)が必要になる。

対策

実装モジュール選択を使う。

結果

MixJuice では concrete product と abstract product をまとめてひとつのクラスにしても、生成するオブジェクト群を選択可能にするというパターンの目的を達成することができる。これは、差分によってひとつのクラスのインターフェースと実装を異なるモジュールに記述することができ、リンク時に実装を選択することができるためである。

ここで述べた対策が適用可能なのはひとつのバイナリがひとつの concrete factory しか必要としない場合である。このような場合は GoF 本に述べられているように典型的である。

p.98 「典型的なアプリケーションでは、部品の集合ごとに ConcreteFactory クラスのインスタンスを 1つしか必要としない」

p.90, line.12-13, 1.Factories as singletons

An application typically needs only one instance of a ConcreteFactory per product family.

構造

structure-08-09:abstractfactory2

module products {
  define class ProductA {
    define abstract ProductA();
    ...
  }
  define class ProductB {
    define abstract ProductB();
    ...
  }
}

module products.implementation1 extends products {
  class ProductA {
    ProductA() {...}
    ...
  }
  class ProductB {
    ProductB() {...}
    ...
  }
}

module products.implementation2 extends products {
  class ProductA {
    ProductA() {...}
    ...
  }
  class ProductB {
    ProductB() {...}
    ...
  }
}

module client extends products {
  ... new ProductA() ...
  ... new ProductB() ...
}

サンプルコード

structure-08-09:abstractfactory_sample

module window_system {
  define class Window {
    define abstract Window();
    ...
  }
  define class ScrollBar {
    define abstract ScrollBar();
    ...
  }
}

module motif extends window_system {
  class Window {
    Window() {...}
    ...
  }
  class ScrollBar {
    ScrollBar() {...}
    ...
  }
}

module presentation_manager extends window_system {
  class Window {
    Window() {...}
    ...
  }
  class ScrollBar {
    ScrollBar() {...}
    ...
  }
}

module client extends window_system {
  ... new Window() ...
  ... new ScrollBar() ...
}

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


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