【PHPデザインパターン】07_Abstract Factory～関連する部品をまとめて作る工場

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

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

> [Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Abstract Factory～関連する部品をまとめて作る工場](http://d.hatena.ne.jp/shimooka/20141215/1418620420)

# 概要
- 「abstract factory」を直訳すると「抽象的な工場」になる。
- 具体的なクラスを明確にすることなく、関連し合うオブジェクトの集まりを生成する。
- クラインアントが利用する工場クラスと部品クラスは抽象化されるので、クライアントは具体的な内容を意識する必要がない。
- 利用する部品クラスの整合性を保つことができる。
- 関連する部品クラスの集まりを容易に切り替えることができる。

## DAOパターンについて
``DAO（Data Access Object）パターン``は、データベースに関する処理内容とそれ以外のアプリケーション固有の処理（ビジネスロジック）とを分けて設計・実装します。

データベースに関する処理を整合性を保ったまま簡単に切り替えたい場合、Abstract Factoryパターンが利用できます。

> [サルでもわかる 逆引きデザインパターン〜第3章 逆引きカタログ　J2EE編〜DAO (Data Access Object)](http://www.nulab.co.jp/designPatterns/designPatterns3/designPatterns3-4.html)

## 構成要素
### AbstractFactoryクラス
- 抽象化された工場クラス。
- 部品であるAbstractProductクラスを生成するクラス。
- 生成するのは具体的な部品であるConcreteProductクラスではない。

### ConcreteFactoryクラス
- AbstractFactoryクラスのサブクラス。
- AbstractFactoryクラスで定義された生成メソッドを実装する。
- 具体的な部品であるConcreteProductクラスを返す。

### AbstractProductクラス
- 抽象化された部品クラス。
- 部品ごとのAPIを定義するクラス。

### ConcreteProductクラス
- AbstractProductクラスのサブクラス。
- ConcreteFactoryクラスから返される具体的な部品クラス。

### Clientクラス
- クライアント側となるクラス。
- 抽象クラスであるAbstractFactoryクラスとAbstractProductクラスのAPIのみを利用する。

# 実演
## 処理の流れ
- 商品情報を登録し、その情報を元にメッセージを出力する。
- 元データの扱い方によりFactoryクラスを分ける。
- Db系のクラスでは、与えられた配列から商品情報を抽出してItemインスタンスを生成する。
- Mock系のクラスでは、直接引数を与えてItemインスタンスを生成する。

## ファイル構造
階層が皆同じで見辛い・・・

```sh
 MyAbstractFactory
   ├── DaoFactory.php
   ├── DbFactory.php
   ├── DbItemDao.php
   ├── Item.php
   ├── ItemDao.php
   ├── MockFactory.php
   ├── MockItemDao.php
   └── my_client.php
```

## ソースコード
### AbstractFactoryクラス
#### DaoFactory.php

```DaoFactory.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

interface DaoFactory
{
    // メソッドはサブクラスで実装させる
    public function createItemDao();
}
```

### ConcreteFactoryクラス
#### DbFactory.php

```DbFactory.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\DaoFactory;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\DbItemDao;

// DaoFactoryインターフェイスのメソッドを実装する
class DbFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new DbItemDao();
    }
}
```

#### MockFactory.php

```MockFactory.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\DaoFactory;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\MockItemDao;

// DaoFactoryインターフェイスのメソッドを実装する
class MockFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new MockItemDao();
    }
}
```

### AbstractProductクラス
#### ItemDao.php

```ItemDao.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

interface ItemDao
{
    // メソッドはサブクラスで実装させる
    public function findById($item_id);
}
```

### ConcreteProductクラス
#### DbItemDao.php
```DbItemDao.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\ItemDao;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\Item;

// ItemDaoインターフェイスのメソッドを実装する
class DbItemDao implements ItemDao
{
    private $items;

    public function __construct()
    {
        // テスト用の配列型データ
        $data_array = array(1, 'orange');
        list($item_id, $item_name) = $data_array;

        // Itemインスタンスを作成する
        $item = new Item($item_id, $item_name);
        // idを添字にして、Itemインスタンスを配列型プロパティに格納する
        $this->items[$item->getId()] = $item;
    }

    // 指定されたidを持つItemインスタンスを返す
    public function findById($item_id)
    {
        if (array_key_exists($item_id, $this->items)) {
            return $this->items[$item_id];
        } else {
            return false;
        }
    }
}
```

#### MockItemDao.php

```MockItemDao.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\ItemDao;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\Item;

// ItemDaoインターフェイスのメソッドを実装する
class MockItemDao implements ItemDao
{
    // 引数$item_idはメソッド内では使用されない
    public function findById($item_id)
    {
        // 単純にItemインスタンスを作成して返す
        $item = new Item(1, 'orange');

        return $item;
    }
}
```

### Modelクラス
商品情報を保有します。
#### Item.php

```Item.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

// 商品情報を扱うクラス
class Item
{
    private $id;
    private $name;

    // コンストラクタで商品情報を登録する
    public function __construct($id, $name)
    {
        $this->id = $id;
        $this->name = $name;
    }

    // 商品情報を取得するメソッド
    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }
}
```

### Clientクラス

#### my_client.php
```my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory;

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

use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\DaoFactory;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\DbFactory;
use DoYouPhp\PhpDesignPattern\AbstractFactory\MyAbstractFactory\MockFactory;

// 指定されたidの商品情報を出力する
function show(DaoFactory $factory)
{
    $item_id = 1;
    $item_dao = $factory->createItemDao();
    // 引数$item_idはDbFactoryクラスでのみ使用される
    $item = $item_dao->findById($item_id);
    printf('ID=%sの商品は「%s」です！%s', $item_id, $item->getName(), '<br>'."\n");
}

echo 'DbFactoryクラスを使用'.'<br>'."\n";
show(new DbFactory());

echo 'MockFactoryクラスを使用'.'<br>'."\n";
show(new MockFactory());
```