通常、インスタンスを作成する場合には、クラス名を指定する。
しかし、クラス名を指定せずにインスタンスを作成したくなる場合がある。
その場合にはクラスからインスタンスを作るのではなく、
インスタンスをコピーして新しいインスタンスを作る。
このようなパターンをPrototypeパターンという
例としては以下のような場合があげられる。
扱うオブジェクトの種類が多すぎて、
1つ1つ別のクラスにしていたら、ソースファイルを多数作成する必要がある場合
生成させたいインスタンスが複雑な過程を経て作られているものの場合。
例えば、グラフィック画dぃタなどでユーザがマウスの操作によって作り上げた図形を現すインスタンス。
インスタンスの生成を特定のフレームワークに依存させたくない場合。
<?php interface Product { function show($string); function createClone(); } ?>
<?php class Manager { private $hashmap; public function __construct() { $this->hashmap = array(); } public function register($name, $proto) { $this->hashmap[$name] = $proto; } public function create($protoname) { $product = $this->hashmap[$protoname]; return $product->createClone(); } }
?>
Productインターフェイスを利用してインスタンスの複製を行うクラス。
$protoは実際のクラスはわからないが、
とにかくProductインターフェイスを実装したクラスのインスタンス。
※さきほどのProductインターフェイスやManagerクラスのソースに、
インスタンスを作るクラスの名前が(後述するMessageBox?クラス、UnderlinePen?クラス)
まったく出てきていない。
ソースの中にクラス名を書くと、そのクラスと密な関係が出来てしまう。
その点、このManagerクラスでは具体的なクラス名が出てこず
Productというインターフェイス名だけを使っており、
このインターフェイスだけがManagerクラスと他のクラスの架け橋になっている。
<?php require_once("Product.class.php"); class MessageBox implements Product { private $decochar; public function __construct($decochar) { $this->decochar = $decochar; } public function show($string) { $length = strlen($string); for ($i = 0; $i < $length + 4; $i++) { echo $this->decochar; } echo "<br />"; echo $this->decochar." ".$string." ".$this->decochar; echo "<br />"; for ($i = 0; $i < $length + 4; $i++) { echo $this->decochar; } echo "<br />"; } public function createClone() { $this_clone = clone $this; if (!($this_clone instanceof Product)) { throw new Exeception(); } return $this_clone; } } ?>
Productクラスのサブクラス。
なお、createCloneメソッドは、自身の複製を行うメソッド。
<?php require_once("Product.class.php"); class UnderlinePen implements Product { private $ulchar; public function __construct($ulchar) { $this->ulchar = $ulchar; } public function show($string) { $length = strlen($string); echo " ".$string." "; echo "<br />"; for ($i = 0; $i < $length; $i++) { echo $this->ulchar; } echo "<br />"; } public function createClone() { $this_clone = clone $this; if (!($this_clone instanceof Product)) { throw new Exeception(); } return $this_clone; } } ?>
Productクラスのサブクラス。
なお、createCloneメソッドは、自身の複製を行うメソッド。
<?php require_once("Manager.class.php"); require_once("UnderlinePen.class.php"); require_once("MessageBox.class.php"); class Main { public function __construct() { $manager = new Manager(); $upen = new UnderlinePen("~"); $mbox = new MessageBox("*"); $sbox = new MessageBox("/"); $manager->register("strong message", $upen); $manager->register("warning box", $mbox); $manager->register("slash box", $sbox); $product1 = $manager->create("strong message"); $product1->show("Hello, world."); echo "<br />"; $product2 = $manager->create("warning box"); $product2->show("Hello, world."); echo "<br />"; $product3 = $manager->create("slash box"); $product3->show("Hello, world."); } :new Main(); ?>
インスタンスをコピーして新しいインスタンスを作るためのメソッドを定める。
サンプルプログラムではProductインターフェイスがこの役をつとめた。
インスタンスをコピーして新しいインスタンスを作るためのメソッドを実装する。
インスタンスをコピーして新しいインスタンスを作るメソッドを実装する。
サンプルプログラムでは3つの雛型が登場した。
これらの雛型をもっと増やしても、プログラムのファイル数は変わらない。
サンプルプログラムでは、インスタンスのコピーを行う部分を
ManagerクラスとProductクラスに閉じ込めているので、これは実現している。
ソースの中にクラス名がないので、再利用をしやすい
参考資料