【PHPデザインパターン】04_Factory Method～生成処理と使用処理を分離する

# 引用記事

この記事を書くきっかけになったブログです。

記事内の解説やソースコードは、こちらのブログと著者の公開リポジトリを参考にしています。

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Factory Method～生成処理と使用処理を分離する](http://d.hatena.ne.jp/shimooka/20141215/1418620242)

# 概要

- オブジェクトを生成するときのインターフェイスだけを規定して、実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。
- 過程は違うが結果は同じ。つまり、生成されるインスタンスはどのクラスのものであれ、同様の「機能」を持っている。
- インスタンス生成用のクラスが存在することから、``Virtual Constructor（仮想的なコンストラクタ）``とも呼ばれている。
- ``Template Methodパターン``の代表的な適用例である。


## 構成要素

### Productクラス
- オブジェクト生成メソッド（工場）で生成されるオブジェクト（製品）のAPIを定義するクラス。
- オブジェクト生成メソッドは、``factory method``とも呼ばれる。

### ConcreteProductクラス
- Productクラスのサブクラス
- 上記APIのメソッドを実装する。
 
### Creatorクラス
- オブジェクト生成メソッドを提供するクラス。
- このメソッドは、Product型のオブジェクトを返す。クライアントの窓口。
- また、あるConcreteProductオブジェクトを返すために、デフォルトの実装がなされる場合もある。

### ConcreteCreatorクラス
- Creatorクラスを継承したサブクラス。


# 実演

## 処理の流れ

- 渡した変数の型（配列か文字列か）でオブジェクトのクラスを分ける。表示方法は同じ。
- ``MyReaderFactoryクラス``はオブジェクト生成のメソッドを提供する。クライアントとの接点。（Creatorクラス）
- クライアント側（``my_client.php``）から上記のオブジェクトを生成する。
- ``MyReaderクラス``でオブジェクト生成に必要な枠（API）を提供する。（Productクラス）
- ``ArrayReaderクラス``と``StringReaderクラス``でMyReaderクラスに決められた枠を実装し、それぞれのオブジェクトを生成する。（ConcreteProductクラス）
- 今回はConcreteCreatorクラスに該当するクラスはなし。


## ファイル構造

```
 MyFactoryMethod
   ├── MyConcreteProduct
   │   ├── ArrayReader.php
   │   └── StringReader.php
   ├── MyReader.php
   ├── MyReaderFactory.php
   └── my_client.php
```

## ソースコード

### my_client.php

```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod;

require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyReaderFactory;

// 型の違う3つのデータを用意する
$array_data   = array('apple', 'orange', 'peach', 'banana');
$string_data = 'fruits';
$int_data = 123;

// 変数の内容を表示させる
function show($data)
{
    // 結果としてMyReaderクラスのインスタンスが生成される
    // 型別のインスタンスが返される
    $factory = new MyReaderFactory();
    $data = $factory->create($data);
    // 型を表示する
    $data->display_type();
    // 変数の内容を表示する
    $data->display_data();
}

// 型別に実行する
show($array_data);
show($string_data);
show($int_data);
```

### MyReaderFactory.php

```MyReaderFactory.php
<?php
namespace DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod;

use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyReader;
use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyConcreteProduct\ArrayReader;
use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyConcreteProduct\StringReader;


// MyReaderクラスのインスタンス生成を行うクラス
class MyReaderFactory
{
    // MyReaderクラスのインスタンスを生成する
    public function create($data)
    {
        $reader = $this->createReader($data);

        return $reader;
    }

    // 変数の型からMyReaderクラスのサブクラスを判定する
    // 変数の型にあったインスタンスを生成する
    private function createReader($data)
    {
        if (is_array($data)) {
            return new ArrayReader($data);

        } elseif (is_string($data)) {
            return new StringReader($data);

        } else {
            die(gettype($data).'型はサポートしていません');

        }
    }
}
```

### MyReader.php

```MyReader.php
<?php
namespace DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod;

// 読み込み機能を表すインターフェイス
// メソッドはサブクラスで実装される
interface MyReader
{
    // 型を表示する
    public function display_type();
    // 変数の内容を表示する
    public function display_data();
}
```

### ArrayReader.php

```ArrayReader.php
<?php
namespace DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyConcreteProduct;

use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyReader;

// 配列の読み込みを行うクラス
class ArrayReader implements MyReader
{
    // 変数をプロパティとして保管
    private $data;

    // コンストラクタ
    public function __construct($data)
    {
        $this->data_name = $data;
    }

    // 型を表示する
    public function display_type()
    {
        echo 'このデータの型は'.gettype($this->data_name).'です'."<br>"."\n";
    }

    // 配列を改行区切りで表示する
    public function display_data()
    {
        foreach ($this->data_name as $value) {
            echo $value."<br>"."\n";
        }
    }
}
```

### StringReader.php

```StringReader.php
<?php
namespace DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyConcreteProduct;

use DoYouPhp\PhpDesignPattern\FactoryMethod\MyFactoryMethod\MyReader;

// 文字列の読み込みを行うクラス
class StringReader implements MyReader
{
    // 変数をプロパティとして保管
    private $data;

    // コンストラクタ
    public function __construct($data)
    {
        $this->data_name = $data;
    }

    // 型を表示する
    public function display_type()
    {
        echo 'このデータの型は'.gettype($this->data_name).'です'."<br>"."\n";
    }

    // 文字列を表示する
    public function display_data()
    {
        echo $this->data_name."<br>"."\n";
    }
}
```