前章のTemplate Methodではスーパークラスで処理の大枠を作り、
サブクラスで具体的な処理を行った。
このパターンをインスタンス生成の場面に適用したのがFactory Methodパターン
Factory Methodパターンではインスタンスの作り方をサブクラスで定めるが、
具体的なクラス名までは定めない。
これによってインスタンス生成の枠組みと、実際の生成を分けて考えることが出来る。
「IDカードを作る工場」を題材にしたプログラムを考えてみる
<?php abstract class Product { public abstract function card_use(); } ?>
「製品」を表すクラスで、抽象メソッドのuseだけが宣言されている。
これの「製品」とは「何はともあれuseできるもの」を規定している。
<?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パターンが使われる。
<?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; } } ?>
<?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; } } ?>
<?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(); ?>
大枠を決める側で、このパターンで生成されるインスタンスが持つべき
インターフェイスを定める抽象クラス。
サンプルプログラムでは、Productクラスがこの役に当たる
Productを生成する抽象クラス。
サンプルプログラムでは、Factoryクラスがこの役に当たる。
Creatorは、実際に生成するConcreteProduct?(サンプルプログラムでいうIDCardクラス)は何も知らない。
知っているのはインスタンス生成のメソッドを呼び出せば、Productが生成されるということだけ。
サンプルプログラムでは、createProductメソッドがインスタンス生成のためのメソッドになっている。
newによるインスタンス生成を、インスタンス生成のためのメソッド呼び出しに変えることによって、
具体的なクラス名により束縛からスーパークラスを解放している。
具体的な製品を定める。
サンプルプログラムでは、IDCardクラスがこの役に当たる
具体的な製品を作るクラスを定める。 サンプルプログラムでは、IDCardFactory?クラスがこの役に当たる
このサンプルプログラムにおいては、
「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パターンも
実際に行っていることの割には、すごく複雑なプログラミングに見える。
これは、設計者の最初の意図とかけ離れた修正が加えられないように、 大枠を決めておくため。
Creator役をつとめるクラスの場合は、
多くの場合Singletonになっていることが多い。
これはプログラムの中で複数のインスタンスが存在する必要が無いため。
ただし、このサンプルプロムはSingletonパターンではない。
Iteratorパターンのiteratorメソッドがインスタンスを作成するときに、
Factory Methodパターンが使われる場合がある。
参考資料