値から参照への変更 (リファクタリング-p179)
リファクタリング
Published: 2018-12-02

目的

「リファクタリング」を理解するためにサンプルコードを PHP で書き換えてみました。

今回は「値から参照への変更」について書きます。

「値から参照への変更」 について

同じインスタンスが多数存在するクラスがある。それらを1つのオブジェクトに置き換えたい。

多くのシステムにおいて、たとえば参照オブジェクトと値オブジェクトというような分類は有効です。

変更前

<?php

class Customer
{
    private $_name;

    public function __construct($name)
    {
        $this->_name = $name;
    }

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

class Order
{
    /* @var Customer */
    private $_customer;

    public function __construct($customerName)
    {
        $this->_customer = new Customer($customerName);
    }

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

    public function setCustomer($customerName)
    {
        $this->_customer = new Customer($customerName);
    }
}

class Client
{
    public static function numberOfOrdersFor($orders, $customer)
    {
        $result = 0;
        foreach ($orders as $order) {
            if ($order->getCustomerName() === $customer) {
                $result++;
            }
        }

        return $result;
    }
}

$orders = [
    new Order('customer1'),
    new Order('customer2'),
    new Order('customer3'),
    new Order('customer2'),
];

echo Client::numberOfOrdersFor($orders, 'customer2') . "\n";

変更後

<?php

class Customer2
{
    private $_name;

    // 説明を簡単にするため、ここに保持する
    private static $_instances;

    private function __construct($name)
    {
        $this->_name = $name;
    }

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

    public static function getNamed($name)
    {
        // 動かすようにした、本来はどこに置くべきか
        self::loadCustomers();

        return self::$_instances[$name];
    }

    private function store()
    {
        self::$_instances[$this->getName()] = $this;
    }

    public static function loadCustomers()
    {
        (new Customer2('customer1'))->store();
        (new Customer2('customer2'))->store();
        (new Customer2('customer3'))->store();
    }
}

class Order2
{
    /* @var Customer2 */
    private $_customer;

    public function __construct($customerName)
    {
        $this->_customer = Customer2::getNamed($customerName);
    }

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

    public function setCustomer($customerName)
    {
        $this->_customer = new Customer2($customerName);
    }
}

class Client2
{
    public static function numberOfOrdersFor($orders, $customer)
    {
        $result = 0;
        foreach ($orders as $order) {
            if ($order->getCustomerName() === $customer) {
                $result++;
            }
        }

        return $result;
    }
}

$orders = [
    new Order2('customer1'),
    new Order2('customer2'),
    new Order2('customer3'),
    new Order2('customer2'),
];

echo Client2::numberOfOrdersFor($orders, 'customer2') . "\n";