Builder Pattern vs Abstract Factory Pattern
どちらもインスタンス生成に関わるパターン。
違い、というか、フレームワーク設計時にこの2つをどう使い分ければいいのか、よくわからなかったので、自分なりにまとめてみた。
Builder Patternは、インスタンスの生成プロセスを抽象化する(Abstract Builder)。プロセスはカプセル化され、それをシンプルなAPIにまとめ、利用可能にする。というわけで、様々な生成プロセスを持つクラス(Concrete Builders)をシンプルなAPIで作れるようになる。
『Createrは汎化されない、Createされる対象が汎化される』と理解してみる。
対して、Abstract Factory Patternは、あるクラス群と、その生成クラスを抽象化する(Abstract Models, Abstract Factory)。個々のクラスの生成プロセスはAPI群として、Abstrtact Factoryが提供する。つまり、インスタンス生成にかかわるAPIはあらかじめ固定される。
『Createrは汎化される(FactoryA, FactoryB, FactoryC,...)、Createされる対象は汎化されない』と理解してみる。
Prototypeパターン
オブジェクトからオブジェクトを生成する設計。
phpではcloneキーワードでオブジェクトのシャローコピーを生成できる。
<? class Widget{ public $header; function __construct(){ $this->header = new Header($title); //<-- headerプロパティはHeaderオブジェクトの参照である } /* * phpのクローンキーワードは、オブジェクトのシャローコピーを行う * ディープコピーを行うときは以下のように__cloneメッソドを実装して、 * それぞれのプロパティのクローンを作成する必要がある */ function __clone(){ $this->header = clone $this->header; } function title(){ return $this->header->title; } } class Header{ public $title = "default title"; } $widget_a = new Widget; echo $widget_a->title() . "<br />"; //#default title $widget_b = clone $widget_a; echo $widget_a->title() . "<br />"; //#default title $widget_a->header->title = "hogehoge"; echo $widget_a->title() . "<br />"; //#hogehoge echo $widget_b->title() . "<br />";//#default titleになる(__cloneが実装されていないとhogehogeのまま) ?>
colinuxでubuntuの環境を作る
ubuntu のイメージを直接colinuxのサイトから落としたやつだとシングルモードでの起動になってしまうので、インストーラー付属のイメージを使う。
ネットワーク接続はTAPで行う。Win側のネットワーク設定手順は割愛。ひとつだけ、ファイアーウォールが接続のジャマをするかもしれないので、注意。
以下、走り書きメモ。
最初のログインはID root, PW rootで。
nanoが入っているので、それを使って、まずはネットワークの設定。
ubuntu側はIP固定にする方向で。
etc/network/initerfacesを編集。んでもって、etc/resolve.confも編集。そして、/etc/init.d/networking restartする。
つぎに、aptの設定。ubuntuのバージョンがおそらく古いので、/etc/apt/sources.listを編集する。
編集の方法は、
deb http://old-releases.ubuntu.com/ubuntu jaunty main restricted universe multiverse deb http://old-releases.ubuntu.com/ubuntu jaunty-updates main restricted universe multiverse deb-src http://old-releases.ubuntu.com/ubuntu/ jaunty-updates main restricted universe multiverse
こんな感じで。詳しくはhttp://dqn.sakusakutto.jp/2011/06/ubuntu-apt-get-update.htmlとかで。
適切にsources.listを編集したら、apt-update apt-upgradeする。
これで、apt-get install hogehogeしまくれる状態になったはず。
Template/ Factory Method
Template method
ベースクラスにテンプレートメソッドを実装、他はサブクラスにデレゲートする、そういう設計
なので、ベースクラスの設計を分かりやすくしておくこと(コメント、ドキュメントとかを作っとく)
要は、デレゲーションさせる設計はテンプレートメソッドなのだと、言ってしまいたい。->ちょっとこれは違うな。デレゲーションは、クラスが他オブジェクトに、自分が受け持つメソッドの処理を引き渡す、すなわち、文字通り委譲する、その移譲するオブジェクトを抱えているという事なのだろう。先にも赤で書いたとおり、「サブクラスにデレゲート」というのが、Tempplate methodの本質か。
Factory Method
インスタンスの作成をファクトリーベースクラスのテンプレートメソッドコールで行う。ファクトリーメソッド(モデルクラスを実際に生成したりする)はサブクラスで実装する。
<?php abstract class ModelBase{ abstract function do_something(); final function must_do_it(){ echo "<br /><br />First of all, let's have a coffee.<br />"; } } class SomeModel extends ModelBase{ function do_something(){ $this->must_do_it(); echo "Next, we have on a chat each other.<br/>"; } } class AnotherModel extends ModelBase{ function do_something(){ $this->must_do_it(); echo "Next, we'll welcome another one.<br/>"; } function do_something_else(){ echo "Something Else<br/>"; } } abstract class FactoryBase{ abstract function factory_method(); final function create(){ //<--クリエータークラス return $this->factory_method();//<-- インスタンス生成をサブクラスにデレゲーションする } } class Factory extends FactoryBase{ function factory_method(){ return new AnotherModel(); } } class AnotherFactory extends FactoryBase{ function factory_method(){ return new SomeModel(); } } $factory_a = new Factory; $factory_b = new AnotherFactory; $model_a = $factory_a->create(); $model_b = $factory_b->create(); $model_a->do_something(); $model_b->do_something(); $model_a->do_something_else(); ?>
Bridge Pattern
機能(function, feature, facility)と実装(implementation)を分ける。
あるクラスに対して、異なる実装が必要な場合に有効な設計手段。
実装の基底クラスをbridgeによって結びつける。bridgeはデレゲーションによって実現される。
そして、機能拡張は浅いほど良い。
<?php class Display{ protected $impl;//<-- ブリッジ function __construct($impl){ $this->impl = $impl; } function open(){ //<--DisplayクラスのAPI. ブリッジを通じて実装される $this->impl->raw_open(); } function content(){ //<--DisplayクラスのAPI. ブリッジを通じて実装される $this->impl->raw_content(); } function close(){ //<--DisplayクラスのAPI. ブリッジを通じて実装される $this->impl->raw_close(); } final function render(){ //<--- 機能拡張しない(final method) $this->open(); $this->content(); $this->close(); } } abstract class DisplayImpl{ //<-- 実装の基底クラス abstract function raw_open(); abstract function raw_content(); abstract function raw_close(); } class SomeDisplayImpl extends DisplayImpl{ //<-- Displayの実装 function raw_open(){ } function raw_content(){ } function raw_close(){ } } class AnotherDisplayImpl extends DisplayImpl{//<--さらに別のDisplayの実装 function raw_open(){ } function raw_content(){ } function raw_close(){ } } class CountDisplay extends Display{ //<-- Displayクラスの拡張クラス function multi_render($times){ $this->open(); for($i = 0;$i< $times;$i++){ $this->render(); } } } $impl_x = new SomeDisplayImpl(); $impl_y = new AnotherDisplayImpl(); $impl_z = new AnotherDisplayImpl(); $disp_a = new Display($impl_x); $disp_b = new Display($impl_y); $disp_c = new CountDisplay($impl_z); $disp_a->render(); $disp_b->render(); $disp_c->multi_render(4); ?>
Adapter Pattern
名前の通り、アダプターの機能。
インターフェースの互換性を持たせるための設計手段。
<? /* * 変換されるクラス */ class Banner{ protected $title = ""; function __construct($arg){ $this->title = $arg; } function show_with_paren(){ echo "((({$this->title})))<br />"; } function show_with_aster(){ echo "***{$this->title}***<br />"; } } /* * アダプターインターフェース */ interface PrintAdapter{ function print_weak();//<-- 新たなインターフェース function print_strong();//<-- 新たなインターフェース } /* * アダプター */ class PrintBannerAdapter extends Banner implements PrintAdapter{ function print_weak(){ $this->show_with_paren();//<-- 二つのインターフェースを変換 } function print_strong(){ $this->show_with_aster();//<-- 二つのインターフェースを変換 } } $print_banner = new PrintBannerAdapter("コーヒーの美味しさについて"); $print_banner->print_strong();//<-- 新たなインターフェースを利用 $print_banner->print_weak();//<-- 新たなインターフェースを利用 echo "<hr />"; /* * アダプター基底クラス */ abstract class AnotherPrintAdapter{ abstract function print_weak();//<-- 新たなインターフェース abstract function print_strong();//<-- 新たなインターフェース } /* * アダプター */ class AnotherPrintBannerAdapter extends AnotherPrintAdapter{ function __construct($title){ $this->banner = new Banner($title); } function print_weak(){ $this->banner->show_with_paren();//<-- 変換前クラスにデレゲート } function print_strong(){ $this->banner->show_with_aster();//<-- 変換前クラスにデレゲート } } $another_print_banner = new AnotherPrintBannerAdapter("ココアの温かさについて"); $another_print_banner->print_strong();//<-- 新たなインターフェースを利用 $another_print_banner->print_weak();//<-- 新たなインターフェースを利用 ?>
Iterator Pattern
なんつうか、IteratorとかAggregateという名前がしっくりこない。Enumerable, Scannableとかのほうがしっくりくる気がする。
イテレーター(以下、スキャナーと書いてしまおう)はスキャンする対象を持っていて、そのインターフェースがhas_next, nextメソッド。集合体はスキャナーをCreateするインターフェースをもち、それがiteratorメソッド。
以下、コード;
<? /* * 集合体のインターフェース * イテレーターインターフェースを持つ */ interface Aggregate{ function iterator(); } /* * 反復動作のインターフェース * 言い換えると、集合体のスキャンに必要なインターフェース */ interface MyIterator{ function has_next(); function next(); } /* * フォトアルバム(写真の集合体) * スキャンクラスを持つ */ class PhotoAlbum implements Aggregate{ protected $photos = array(); //<--集合体はカプセル化されている protected $iterator; function __construct($args){ $this->photos = $args; } function iterator(){ return new PhotoAlbumIterator($this); } function odd_iterator(){ return new PhotoAlbumOddIterator($this); } function get_photo_at($index){ //<--ゲッター return $this->photos[$index]; } function size(){ return count($this->photos); } } /* * アルバムのイテレーター */ class PhotoAlbumIterator implements MyIterator{ protected $photo_album; //<-- 数え上げる対象(PhotoAlbum)を知っている protected $index; //<-- 数え上げる為に必要 function __construct($arg){ $this->photo_album = $arg; $this->index = 0; } function has_next(){ return $this->index < $this->photo_album->size(); } function next(){ $photo = $this->photo_album->get_photo_at($this->index); ++ $this->index; return $photo; } } class PhotoAlbumOddIterator extends PhotoAlbumIterator{ function next(){ $photo = $this->photo_album->get_photo_at($this->index); $this->index = $this->index + 2; return $photo; } } $books = array("コーヒーの写真", "コーラの写真", "ファンタの写真", "山の写真", "ダイエットコークの写真"); $photo_album = new PhotoAlbum($books); $iterator = $photo_album->iterator(); while($iterator->has_next()){ echo $iterator->next() . "<br />"; } echo "<hr />"; $odd_iterator = $photo_album->odd_iterator(); while($odd_iterator->has_next()){ echo $odd_iterator->next() . "<br />"; } ?>