はい、お勉強かねがね今さらデザインパターンです。
PHP5.3系ならでは、な実装ができないものかなぁと考えてみます。
まずはAbstract Factoryからです
まずは抽象クラスから。
Factoryクラスです。
abstractfactory/factory/Factory.php
<?php namespace abstractfactory\factory; /** * Abstract Factory * @author $Author$ * */ abstract class Factory { /** * Factory type 1 * @var integer */ const TYPE_1 = 1; /** * Factory type 2 * @var integer */ const TYPE_2 = 2; /** * Return factory * @return Factory */ public static function getFactory($type) { if($type == self::TYPE_1) { return new ConcreteFactory1(); } else if($type == self::TYPE_2) { return new ConcreteFactory2(); } throw new \Exception('Argument of ' . __CLASS__ . '::' . __METHOD__ . '() must be constant of ' . __CLASS__ ); } /** * Create Product1 * @return Product1 */ abstract public function createProduct1(); /** * Product2 * @return Product2 */ abstract public function createProduct2(); }
次にProduct11クラス。
Productは2つ扱うことにしてみます。
abstractfactory/product/Product1.php
<?php namespace abstractfactory\product; /** * Abstract Product1 * @author $Author$ */ abstract class Product1 { /** * Output class name of this Product */ abstract public function showName(); }
Product2クラス。
abstractfactory/product/Product2.php
<?php namespace abstractfactory\product; /** * Abstract Product2 * @author $Author$ */ abstract class Product2 { /** * Output class name of this Product */ abstract public function showName(); }
そしてFactoryを使用するClientクラス。
abstractfactory/Client.php
<?php namespace abstractfactory; /** * Factory Client * @author $Author$ */ class Client { /** * Handle Products of specified type Factory * @param integer $type */ public function handleProduct($type) { try { $factory = factory\Factory::getFactory($type); $factory->createProduct1()->showName(); $factory->createProduct2()->showName(); } catch (\Exception $e) { throw $e; } } }
じゃあ実装クラスです。
Factoryクラスの実装その1である、ConcreteFactory1クラス。
abstractfactory/factory/ConcreteFactory1.php
<?php namespace abstractfactory\factory; /** * ConcreteFactory1 * @author $Author$ */ class ConcreteFactory1 extends Factory { /** * Create ConcreteProduct1_1 * @return ConcreteProduct1_1 */ public function createProduct1() { return new \abstractfactory\product\ConcreteProduct1_1(); } /** * Create ConcreteProduct1_2 * @return ConcreteProduct1_2 */ public function createProduct2() { return new \abstractfactory\product\ConcreteProduct1_2(); } }
Factoryクラスの実装その2である、ConcreteFactory2クラス。
abstractfactory/factory/ConcreteFactory2.php
<?php namespace abstractfactory\factory; /** * ConcreteFactory2 * @author $Author$ */ class ConcreteFactory2 extends Factory { /** * Create ConcreteProduct2_1 * @return ConcreteProduct2_1 */ public function createProduct1() { return new \abstractfactory\product\ConcreteProduct2_1(); } /** * Create ConcreteProduct2_2 * @return ConcreteProduct2_2 */ public function createProduct2() { return new \abstractfactory\product\ConcreteProduct2_2(); } }
ConcreteFactory1クラスが生成するProduct1の実装クラスであるConcreteProduct1_1クラス。
abstractfactory/product/ConcreteProduct1_1.php
<?php namespace abstractfactory\product; /** * Concrete Product1_1 * @author $Author$ */ class ConcreteProduct1_1 extends Product1 { /** * Return class name of this Product * @return string */ public function showName() { print __CLASS__ . PHP_EOL; } }
ConcreteFactory1クラスが生成するProduct2の実装クラスであるConcreteProduct1_2クラス。
abstractfactory/product/ConcreteProduct1_2.php
<?php namespace abstractfactory\product; /** * Concrete Product1_2 * @author $Author$ */ class ConcreteProduct1_2 extends Product2 { /** * Return class name of this Product * @return string */ public function showName() { print __CLASS__ . PHP_EOL; } }
ConcreteFactory2クラスが生成するProduct1の実装クラスであるConcreteProduct2_1クラス。
abstractfactory/product/ConcreteProduct2_1.php
<?php namespace abstractfactory\product; /** * Concrete Product2_1 * @author $Author$ */ class ConcreteProduct2_1 extends Product1 { /** * Return class name of this Product * @return string */ public function showName() { print __CLASS__ . PHP_EOL; } }
ConcreteFactory2クラスが生成するProduct2の実装クラスであるConcreteProduct2_2クラス。
abstractfactory/product/ConcreteProduct2_2.php
<?php namespace abstractfactory\product; /** * Concrete Product2_2 * @author $Author$ */ class ConcreteProduct2_2 extends Product2 { /** * Return class name of this Product * @return string */ public function showName() { print __CLASS__ . PHP_EOL; } }
以上でクラスは出そろったので、実行ファイル
abstractfactory/abstractfactory.php
<?php namespace abstractfactory; define('DIR', dirname(__FILE__)); require_once(DIR . DIRECTORY_SEPARATOR . 'Client.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'factory' . DIRECTORY_SEPARATOR . 'Factory.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'factory' . DIRECTORY_SEPARATOR . 'ConcreteFactory1.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'factory' . DIRECTORY_SEPARATOR . 'ConcreteFactory2.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'Product1.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'Product2.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'ConcreteProduct1_1.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'ConcreteProduct1_2.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'ConcreteProduct2_1.php'); require_once(DIR . DIRECTORY_SEPARATOR . 'product' . DIRECTORY_SEPARATOR . 'ConcreteProduct2_2.php'); $client = new Client(); try { $client->handleProduct(factory\Factory::TYPE_1); } catch (\Exception $e) { print $e->getMessage() . PHP_EOL; } try { $client->handleProduct(factory\Factory::TYPE_2); } catch (\Exception $e) { print $e->getMessage() . PHP_EOL; } try { $client->handleProduct(999); } catch (\Exception $e) { print $e->getMessage() . PHP_EOL; }
で、実行ファイルを実行すると以下の様な出力が得られます。
abstractfactory\product\ConcreteProduct1_1 abstractfactory\product\ConcreteProduct1_2 abstractfactory\product\ConcreteProduct2_1 abstractfactory\product\ConcreteProduct2_2 Argument of abstractfactory\factory\Factory::abstractfactory\factory\Factory::getFactory() must be constant of abstractfactory\factory\Factory
残念ながらPHP5.3らしいことって何もできなかったですね^^;
名前空間を入れてみた、とか機能とはあんまり関係ないことだけです。
Abstract FactoryパターンだとClientがFactoryとProductの実装を知る必要がないので、
FactoryやProductのサブクラスが増えた場合には、FactoryとProductのサブクラスを実装して
Clientを使うクラスで使用するFactoryの識別(Factory::TYPE_n)を切り替える処理を実装すれば
他のクラスは便利に再利用できそうです。
大切なのはClientクラスは抽象クラスであるFactoryとProductしか扱わないこと。
ここに実装クラスが入ってくると、このパターンの意味がなくなってしまいます。
そして注意が必要なのは、Factoryの識別(Factory::TYPE_n)を誰が判断するか、ですね。