本稿では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