PHP でデザインパターン (Abstract Factory)
PHP デザインパターン
Published: 2018-10-21

Abstract Factory パターンとは

具体的なクラスを明確にすることなく、関連し合うオブジェクトの集まりを生成するパターン

オブジェクト指向プログラミングでは、具体的なクラスではなくインターフェースや抽象化されたクラスのAPIに対してプログラミングすることが重要になります

Abstract Factory パターンのメリット

  • 具体的なクラスをクライアントから隠蔽する
  • 利用する部品群の整合性を保つ
  • 部品群の単位で切り替えができる

実装例

<?php
interface DaoFactory
{
    public function createItemDao();
    public function createOrderDao();
}

class DbFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new DbItemDao();
    }

    public function createOrderDao()
    {
        return new DbOrderDao($this->createItemDao());
    }
}

class MockFactory implements DaoFactory
{
    public function createItemDao()
    {
        return new MockItemDao();
    }

    public function createOrderDao()
    {
        return new MockOrderDao();
    }
}

interface ItemDao
{
    public function findById($item_id);
}

interface OrderDao
{
    public function findById($order_id);
}

class DbItemDao implements ItemDao
{
    const ITEM_DATA = [
        1 => '商品1',
        2 => '商品2',
        3 => '商品3',
    ];
    private $items;
    public function __construct()
    {
        $this->items = [];

        foreach (self::ITEM_DATA as $key => $val) {
            $item = new Item($key, $val);
            $this->items[$item->getId()] = $item;
        }
    }

    public function findById($item_id)
    {
        if (array_key_exists($item_id, $this->items)) {
            return $this->items[$item_id];
        }

        return null;
    }
}

class DbOrderDao implements OrderDao
{
    const ORDER_DATA = [
        1 => '3',
        2 => '1,3',
        3 => '1,2,3',
        4 => '2',
        5 => '2,3',
    ];

    private $orders;
    public function __construct(ItemDao $item_dao)
    {
        $this->orders = [];
        foreach (self::ORDER_DATA as $key => $val) {
            $order = new Order($key);
            foreach (explode(',', $val) as $item_id) {
                $item = $item_dao->findById($item_id);
                if (!is_null($item)) {
                    $order->addItem($item);
                }
            }
            $this->orders[$order->getId()] = $order;
        }
    }

    public function findById($order_id)
    {
        if (array_key_exists($order_id, $this->orders)) {
            return $this->orders[$order_id];
        }

        return null;
    }
}

class MockItemDao implements ItemDao
{
    public function findById($item_id)
    {
        $item = new Item($item_id, 'dummy item');
        return $item;
    }
}

class MockOrderDao implements OrderDao
{
    public function findById($order_id)
    {
        $order = new Order($order_id);
        $order->addItem(new Item('99', 'dummy item 99'));
        $order->addItem(new Item('99', 'dummy item 99'));
        $order->addItem(new Item('98', 'dummy item 98'));

        return $order;
    }
}

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;
    }
}

class Order
{
    private $id;
    private $items;

    public function __construct($id)
    {
        $this->id = $id;
        $this->items = [];
    }

    public function addItem(Item $item)
    {
        $id = $item->getId();
        if (!array_key_exists($id, $this->items)) {
            $this->items[$id]['object'] = $item;
            $this->items[$id]['amount'] = 0;
        }
        $this->items[$id]['amount']++;
    }

    public function getItems()
    {
        return $this->items;
    }

    public function getId()
    {
        return $this->id;
    }
}

// // DbFactory
$factory = new DbFactory();
// MockFactory
// $factory = new MockFactory();
$item_id = 1;
$item_dao = $factory->createItemDao();
$item = $item_dao->findById($item_id);
//
echo sprintf("ITEM ID: %d Name: %s\n", $item_id, $item->getName());

$order_id = 3;
$order_dao = $factory->createOrderDao();
$order = $order_dao->findById($order_id);

echo sprintf("ORDER ID: %d\n", $order_id);

foreach ($order->getItems() as $item) {
    echo sprintf("ITEM NAME: %s, AMOUNT: %d\n", $item['object']->getName(), $item['amount']);
}

参考