読者です 読者をやめる 読者になる 読者になる

PHP7でデザインパターン入門4/23 Factory Methodパターン

Factory Method パターンとは

Factory Method パターンは、他のクラスのコンストラクタをサブクラスで上書き可能な自分のメソッドに置き換えることで、 アプリケーションに特化したオブジェクトの生成をサブクラスに追い出し、クラスの再利用性を高めることを目的とする。

Factory Method パターン - Wikipedia

インスタンスを生成するfactory(工場)をTempalte Methodパターンで構成したものが、Factory Methodパターンになる。 Factory Methodパターンでは、インスタンスの作り方をスーパークラス側で定めるが、具体的なクラス名は定めない。 具体的な部分は、すべてサブクラス側で行う。これによってインスタンス生成の枠組みと実際のインスタンス生成のクラスを分けて考えることができるようになる。

example

以下サンプルプログラム。身分証明書カード(IDカード)を作る工場を題材としたもの。4つのクラスが登場する

  • Product
  • Factory
  • IDCard
  • IDCardFactory

Product 抽象クラス

<?php
abstract class Product {
    public abstract function use();
}

Factory 抽象クラス

<?php
abstract class Factory {
    public final function create(string $owner): Product {
        $p = $this->createProduct($owner);
        $this->registerProduct($p);
        return $p;
    }
    protected abstract function createProduct(string $owner): Product ;
    protected abstract function registerProduct(Product $product);
}

Productを継承したIDCardクラス

<?php
class IDCard extends Product {
    private $owner;

    public function __construct (string $owner) {
        echo ($owner . "のカードを作ります。" . PHP_EOL);
        $this->owner = $owner;
    }
    public function use() {
        echo ($this->owner . "のカードを使います。" . PHP_EOL);
    }
    public function getOwner(): string {
        return $this->owner;
    }
}

Factoryを継承したIDCardFactoryクラス

<?php
class IDCardFactory extends Factory {
    private $owners = [];

    protected function createProduct(string $owner): Product {
        return new IDCard($owner);
    }
    protected function registerProduct(Product $product) {
        $this->owners[] = $product->getOwner();
    }
    public function getOwners(): array {
        return $this->owners;
    }
}

main

<?php
$factory = new IDCardFactory();
$card1 = $factory->create("member01");
$card2 = $factory->create("member02");
$card3 = $factory->create("member03");
$card1->use();
$card2->use();
$card3->use();
foreach ($factory->getOwners() as $k => $owner) {
    echo "{$k}:{$owner}" . PHP_EOL;
}

実行結果

member01のカードを作ります。
member02のカードを作ります。
member03のカードを作ります。
member01のカードを使います。
member02のカードを使います。
member03のカードを使います。
0:member01
1:member02
2:member03

役割

Productクラス

フレームワーク側。このパターンで生成されるインスタンスが持つべきインターフェースを定める抽象クラス

Factoryクラス

フレームワーク側。Productを生成する抽象クラス。具体的な内容はConcreteCreator役(IDCardFactoryクラス)が定める。

IDCardクラス

具体的な内容を定める側。具体的なProductを定める

IDCardFactoryクラス

具体的な内容を定める側。具体的な製品を作るクラスを定める。

まとめ

フレームワーク(Factory, Product)と具体的な製品を定めるクラス(IDCardFactory, IDCard)に分離することで、全く別の「製品」と「工場」を作る場合にも同じフレームワークが利用できる。

ソースコード github.com