PHP5でデザインパターン − FactoryMethodパターン
今日のお題はFactoryMethodパターン。オブジェクトを生成する際に、製品(Product)と工場(Factory)を分離します。Productのインスタンス生成をクライアントではなく、Factoryにカプセル化することで拡張性と柔軟性を持った設計にします。クラス図的にはこんな感じでいいのかな?
今回は『HeadFirstデザインパターン』のサンプルをそのまま実装する形にしています。スクリプトはピザクラスと、それを生成するファクトリクラスで構成されます。
まずは、Productとなるピザの抽象クラスとそれを実装するサブクラス(NYスタイルのチーズピザとシカゴスタイルのチーズピザ)。
Pizza.php
<?php /** * FactoryMethodパターンによるPizza */ /** * Pizza */ abstract class Pizza { protected $name; protected $dough; protected $sauce; protected $toppings; public function prepare() { echo "--- ".$this->name."を作っています ---<br>\n"; echo "<ol><li>".$this->name."を下処理\n"; echo "<ul><li>生地をこねる…".$this->dough."</li>\n"; echo "<li>ソースを追加…".$this->sauce."</li>\n"; echo "<li>トッピングを追加:<br>\n"; foreach ($this->toppings as $topping) { echo " ".$topping."<br>\n"; } echo "</li></ul>\n"; } public function bake() { echo "<li>350度で25分間焼く</li>\n"; } public function cut() { echo "<li>ピザを扇形に切り分ける</li>\n"; } public function box() { echo "<li>PizzaStoreの正式な箱にピザを入れる</li></ol>\n"; } public function getName() { return $this->name; } } /** * NYStyleCheesePizza */ class NYStyleCheesePizza extends Pizza { public function __construct() { $this->name = 'ニューヨークスタイルのソース&チーズピザ'; $this->dough = '薄いクラスト生地'; $this->sauce = 'マリナラソース'; $this->toppings[] = '粉レッジャーノチーズ'; } } /** * ChicagoStyleCheesePizza */ class ChicagoStyleCheesePizza extends Pizza { public function __construct() { $this->name = 'シカゴスタイルのディープディッシュチーズピザ'; $this->dough = '極厚クラスト生地'; $this->sauce = 'プラムトマトソース'; $this->toppings[] = '刻んだモツァレラチーズ'; } public function cut() { echo "<li>ピザを四角形に切り分ける</li>\n"; } }
次に、Factoryとなるピザ店の抽象クラスとそれを実装するサブクラス(NYスタイルのピザ店とシカゴスタイルのピザ店)。どちらもメニューはチーズピザしかありません。
PizzaStore.php
<?php /** * FactoryMethodパターンによるPizzaStore */ /** * PizzaStore */ abstract class PizzaStore { public function orderPizza($type) { $pizza = $this->createPizza($type); $pizza->prepare(); $pizza->bake(); $pizza->cut(); $pizza->box(); return $pizza; } abstract protected function createPizza($type); } /** * NYPizzaStore */ class NYPizzaStore extends PizzaStore { protected function createPizza($item) { switch ($item) { case 'チーズ': return new NYStyleCheesePizza(); break; } } } /** * ChicagoPizzaStore */ class ChicagoPizzaStore extends PizzaStore { protected function createPizza($item) { switch ($item) { case 'チーズ': return new ChicagoStyleCheesePizza(); break; } } }
そして、クライアントとなるテストスクリプト。
PizzaTest.php
<?php /** * FactoryMethodパターンのテスト */ require_once 'Pizza.php'; require_once 'PizzaStore.php'; $nyStore = new NYPizzaStore(); $chicagoStore = new ChicagoPizzaStore(); $pizza = $nyStore->orderPizza('チーズ'); echo 'イーサンの注文は'.$pizza->getName()."<br><br>\n"; $pizza = $chicagoStore->orderPizza('チーズ'); echo 'ジョエルの注文は'.$pizza->getName()."<br><br>\n";
実際の動作結果はこちら。
この程度のコードであればFactoryの実装をPizzaクラスやクライアントに盛り込んでしまっても問題ありませんが、拡張性がまるで違います。ピザの種類を増やすには新たなピザのクラスを作成し、それを取り扱うピザ店のcreatePizzaメソッドでインスタンス化します。ピザの材料が変わる場合は、そのピザクラスだけの修正で済みます。ピザ店を増やすには、新たなピザ店のクラスを作成します。この設計は、ピザとピザ店どちらの拡張にも耐えられる構造を持っています。
Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本
- 作者: Eric Freeman,Elisabeth Freeman,Kathy Sierra,Bert Bates,佐藤直生,木下哲也,有限会社福龍興業
- 出版社/メーカー: オライリージャパン
- 発売日: 2005/12/02
- メディア: 大型本
- 購入: 14人 クリック: 362回
- この商品を含むブログ (98件) を見る
この本の日本語訳がいかにも「英語を訳しました」的な雰囲気なので、つられて妙な日本語になってきました。