PHP
PHPでオブジェクト指向
PHPのデザインパターン

PHP: イミュータブルなオブジェクトの実装方法

本稿ではPHPでイミュータブルなオブジェクトを実装する方法を紹介する。

なお、本稿で実装したサンプルコードはGitHubで公開しているので必要であれば参考にしてほしい。

まずはミュータブル(変更可能)なオブジェクトの実装を見てみよう。

Product.php
final class Product
{
    /**
     * @var int
     */
    private $price;

    public function __construct(int $price)
    {
        $this->price = $price;
    }

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

    public function setPrice(int $price): void
    {
        $this->price = $price;
    }
}

このミュータブルなProductクラスでは、setPriceメソッドを呼ぶと$priceプロパティの状態が変更される。

$product = new Product(100);
echo $product->getPrice(); //=> 100

$product->setPrice(300);
echo $product->getPrice(); //=> 300

このsetPriceメソッドのように、呼び出すことでオブジェクトの状態を変更するメソッドのことを「破壊的なメソッド」という。イミュータブル(変更不可)なオブジェクトには、破壊的なメソッドが存在しないという特徴がある。

それでは、ミュータブルなProductクラスをイミュータブルなクラスに作り変えてみよう。

final class Product
{
    // ...

    public function setPrice(int $price): self
    {
        return new self($price);
    }
}

破壊的なメソッドsetPrice$priceプロパティに値を代入するものだったが、イミュータブルなProductクラスのメソッドでは、その代りに新しい$priceを持ったProductクラスを生成して返す実装になる。完成形のコードは次のようになる:

Product.php
final class Product
{
    /**
     * @var int
     */
    private $price;

    public function __construct(int $price)
    {
        $this->price = $price;
    }

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

    public function setPrice(int $price): self
    {
        return new self($price);
    }
}

イミュータブルなProductクラスでは、setPriceを実行してももとのインスタンスの状態が変わることがない:

$product1 = new Product(100);
echo $product1->getPrice(); //=> 100

$product2 = $product1->setPrice(300);
echo $product1->getPrice(); //=> 100
echo $product2->getPrice(); //=> 300

続き: PHP: 属性が多いイミュータブルなオブジェクトの実装方法