第5章


Factory Methodパターン(インスタンス生成をサブクラスに任せる)

「Factory Method」とは?

前章のTemplate Methodではスーパークラスで処理の大枠を作り、
サブクラスで具体的な処理を行った。

このパターンをインスタンス生成の場面に適用したのがFactory Methodパターン

Factory Methodパターンではインスタンスの作り方をサブクラスで定めるが
具体的なクラス名までは定めない
これによってインスタンス生成の枠組みと、実際の生成を分けて考えることが出来る。

Factory Methodの具体例 (サンプルプログラム)

「IDカードを作る工場」を題材にしたプログラムを考えてみる

Productクラス

<?php

abstract class Product {
    public abstract function card_use();
}

?>

「製品」を表すクラスで、抽象メソッドのuseだけが宣言されている。
これの「製品」とは「何はともあれuseできるもの」を規定している。

Factoryクラス

<?php

abstract class Factory {

    public $owner;
    protected $product;

    public function __construct() {
       return $this->product = new Product();
    }

    public final function create($owner) {

        $this->product = $this->createProduct($owner);
        $this->registerProduct($this->product);
        return $this->product;

    }

    protected abstract function createProduct($owner);
    protected abstract function registerProduct($product);

}

?>

このクラスではTemplate Methodパターンが使われている。
cretaeProduct(), registerProduct()の抽象メソッドを作り、
実際の具体的な振る舞いはサブクラスで実装している。

Factory Methodパターンであれば、
インスタンス生成にTemplate Methodパターンが使われる。


IDCardクラス

<?php

require_once("Product.class.php");

class IDCard extends Product {

    public $owner;

    public function __construct($owner) {
         echo $owner ."のカードを作ります。<br />";
         $this->owner = $owner;
    }

    public function card_use() {
        echo $this->owner ."のカードを使います。<br />";
    }

    public function getOwner() {
        return $this->owner;
    }
}

?>

IDCardFactory?

<?php

require_once("Factory.class.php");
require_once("IDCard.class.php");

class IDCardFactory extends Factory {

    public $owner; 
    public $prduct;

    public function __construct() {
        $this->owners = array();
    }

    protected function createProduct($owner) {
        return new IDCard($owner);
    }

    protected function registerProduct($product) {
        $this->owners[] = $product->getOwner();
    }

    public function getOwners() {
        return $this->owners;
    }
}

?>

Mainクラス

<?php

require_once("IDCardFactory.class.php");

class Main {

    public function main() {
        $factory = new IDCardFactory();

        $card1 = $factory->create("taiga1002");
        $card2 = $factory->create("gessy0129");
        $card3 = $factory->create("cutmail");

        $card1->card_use();
        $card2->card_use();
        $card3->card_use();

    }
}

new Main();

?>

Factory Methodパターンにでてくるもの

Product(製作者)

大枠を決める側で、このパターンで生成されるインスタンスが持つべき インターフェイスを定める抽象クラス。
サンプルプログラムでは、Productクラスがこの役に当たる


Creator(製作者)

Productを生成する抽象クラス。
サンプルプログラムでは、Factoryクラスがこの役に当たる。

Creatorは、実際に生成するConcreteProduct?(サンプルプログラムでいうIDCardクラス)は何も知らない。
知っているのはインスタンス生成のメソッドを呼び出せば、Productが生成されるということだけ。
サンプルプログラムでは、createProductメソッドがインスタンス生成のためのメソッドになっている
newによるインスタンス生成を、インスタンス生成のためのメソッド呼び出しに変えることによって
具体的なクラス名により束縛からスーパークラスを解放している

ConcreteProduct?

具体的な製品を定める。
サンプルプログラムでは、IDCardクラスがこの役に当たる

具体的な製品を作るクラスを定める。 サンプルプログラムでは、IDCardFactory?クラスがこの役に当たる

Factory Methodパターンの利点

フレームワークと肉付け

このサンプルプログラムにおいては、
「Productクラス」と「Factoryクラス」というフレームワークと、
それぞれを肉付けする「IDCardクラス」と「IDCardFactory?クラス」に分かれる。

この場合、IDCardクラスとIDCardFactory?クラスを変えて、
別の製品」を生成することができる。

この「Productクラス」と「Factoryクラス」というフレームワークは、
肉付けしたクラスに依存していない


インスタンス生成方法

サンプルプログラムでは、FactoryクラスのcreateProductメソッドは
抽象メソッドになっている。
つまり、サブクラスでの実装が期待されている。

このcreateProductメソッドの記述方法は以下の3通りが考えられる。

抽象メソッドにする

サンプルプログラムと同じ方法。
抽象メソッドにすると、サブクラスで実装しなければならなくなる。
実装されなかった場合にはエラーとなる。

デフォルトの実装を用意しておく

<?php

class Factory {

 …
   public function createProduct($owner) {
        return new Product($owner);
    }
  …

サブクラスで実装しなかった場合には、このデフォルトの実装が使われる。
ただし、この場合はProductクラスを直接newしているので、
Productクラスを抽象クラスにすることはできない。

エラーにする

デフォルトの実装内容をエラーにしておくと、
サブクラスで実装されなかった場合にはすぐエラーになる。

<?php

class Factory {

 …
   public function createProduct($owner) {
        throw new FactoryMethodExeception();
    }
  …

パターン利用と開発者間の意思疎通

Template Methodパターンも、Factory Methodパターンも
実際に行っていることの割には、すごく複雑なプログラミングに見える。

これは、設計者の最初の意図とかけ離れた修正が加えられないように、 大枠を決めておくため。


関連しているパターン

Template Methodパターン

Singletonパターン

Creator役をつとめるクラスの場合は、
多くの場合Singletonになっていることが多い。
これはプログラムの中で複数のインスタンスが存在する必要が無いため。

ただし、このサンプルプロムはSingletonパターンではない。

Iteratorパターン

Iteratorパターンのiteratorメソッドがインスタンスを作成するときに、
Factory Methodパターンが使われる場合がある。


参考資料

  • Java言語で学ぶデザインパターン入門