【PHPデザインパターン】09_Builder~生成の手順と手段を分離する


引用記事

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

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

Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Builder~生成の手順と手段を分離する

概要

  • 「手順」と「材料」を分けておき、同じ手順で異なるオブジェクトを生成させる。
  • オブジェクトを「何を生成するか」と「どのように生成するか」に分離する。

構成要素

Builderクラス

オブジェクトの「生成手段」を提供するクラス群で最上位に位置するクラス。

ConcreteBuilderクラス

  • Builderクラスで提供されるAPIを実装するサブクラス。
  • 生成したオブジェクトを取得するためのメソッドを提供する。

Directorクラス

  • Builderクラスで定義されたAPIを使ってオブジェクトを生成するクラス。

Productクラス

  • 最終的に生成されるオブジェクトのクラス。

実演

処理の流れ

  • 配列型の商品データを受け取り、Itemオブジェクトを生成する。
  • Itemオブジェクトからメッセージを生成する。
  • Productクラスに相当するクラスはない?(生成されるのはItemオブジェクトの配列)

ファイル構造

MyBuilder
  ├── ArrayItemBuilder.php
  ├── Item.php
  ├── ItemBuilder.php
  ├── ItemDirector.php
  └── my_client.php

ソースコード

Builderクラス

ItemBuilder.php

ItemBuilder.php
<?php
namespace DoYouPhp\PhpDesignPattern\Builder\MyBuilder;

// Builderクラスに相当する
// インターフェイスのため、メソッドの実装はサブクラスで行う
interface ItemBuilder
{
    public function parse($data);
}

ConcreteBuilderクラス

ArrayItemBuilder.php

ArrayItemBuilder.php
<?php
namespace DoYouPhp\PhpDesignPattern\Builder\MyBuilder;

use DoYouPhp\PhpDesignPattern\Builder\MyBuilder\ItemBuilder;
use DoYouPhp\PhpDesignPattern\Builder\MyBuilder\Item;

// ConcreteBuilderクラスに相当する
// インターフェイスで定義されたメソッドを実装する
// オブジェクトの具体的な生成手段を提供し、生成されたオブジェクトを取得する
// 生成手段を変更したい場合は、このクラスを別のConcreteBuilderクラスへ切り替えれば良い
class ArrayItemBuilder implements ItemBuilder
{
    // 受け取った配列データをItemオブジェクトの配列として生成する
    public function parse($data)
    {
        foreach ($data as $value) {
            $list[] = new Item($value['id'], $value['name'], $value['price']);
        }

        return $list;
    }
}

Directorクラス

ItemDirector.php

ItemDirector.php
<?php
namespace DoYouPhp\PhpDesignPattern\Builder\MyBuilder;

use DoYouPhp\PhpDesignPattern\Builder\MyBuilder\ItemBuilder;

// Directorクラスに相当する
// オブジェクトの生成手順を提供する
class ItemDirector
{
    private $builder;
    private $data;

    // 第1引数はItemBuilderオブジェクト
    // 第2引数はテストデータ
    // それぞれをプロパティとしてセットする
    public function __construct(ItemBuilder $builder, $data)
    {
        $this->builder = $builder;
        $this->data = $data;
    }

    // ArrayItemBuilderで実装されているメソッドを使用したメソッド
    // Itemオブジェクトの配列を返す
    public function getItem()
    {
        $item_list = $this->builder->parse($this->data);

        return $item_list;
    }
}

Modelクラス

Item.php

Item.php
<?php
namespace DoYouPhp\PhpDesignPattern\Builder\MyBuilder;

// 1つのItemを表すクラス
class Item
{
    private $id;
    private $name;
    private $price;

    // コンストラクタ
    // 各プロパティをセットする
    public function __construct($id, $name, $price)
    {
        $this->id    = $id;
        $this->name  = $name;
        $this->price = $price;
    }

    // 各プロパティの値を呼び出すメソッド
    public function getId()
    {
        return $this->id;
    }

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

    public function getPrice()
    {
        return $this->price;
    }
}

Clientクラス

my_client.php

my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Builder\MyBuilder;

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

use DoYouPhp\PhpDesignPattern\Builder\MyBuilder\ArrayItemBuilder;
use DoYouPhp\PhpDesignPattern\Builder\MyBuilder\ItemDirector;

// ItemBuilderインスタンスを生成する
// 配列形式のテストデータを作成する
$builder = new ArrayItemBuilder();
$data_array = array(
    0 => array(
        'id'    => 1, 
        'name'  => 'apple', 
        'price' => 100,
    ),
    1 => array(
        'id'    => 2, 
        'name'  => 'orange', 
        'price' => 80,
    ),
);

// 第1引数はItemBuilderインスタンス
// 第2引数はテストデータ
$director = new ItemDirector($builder, $data_array);
foreach ($director->getItem() as $item) {
    printf('ID:%d, 商品名:%s, 価格:%d%s', $item->getId(), $item->getName(), $item->getPrice(),'<br>'."\n");
}