Edited at

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: 属性が多いイミュータブルなオブジェクトの実装方法