PHP5.3 de デザパタ(Abstract Factory)

はい、お勉強かねがね今さらデザインパターンです。
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)を誰が判断するか、ですね。

Category: PHP | Posted on: 2009/12/07 18:37 | Viewed: 202

Comments

No comments yet

Add Comment

:

:
:

TrackBacks

このエントリにトラックバックはありません

TrackBack URL

http://www.azul.systems-noel.jp/trackback/item_83.html