ドメイン駆動設計本を読んでいると「値オブジェクトはインスタンス自体を識別する必要はなく不変である」という表現がされていました。
今回は、値オブジェクトについてなぜ不変でないといけないのか実際のコードで表現してみようと思います。
コード実装
class Money
{
private $amount;
public function __construct($amount)
{
$this->amount = $amount;
}
public function add($value)
{
return new Money($this->amount + $value);
}
}
今回は例としてMoneyクラスを作りました。
addメソッドにおいて新しいインスタンスを作成していますね。
このコードは値オブジェクトとして正しいコードになっています。
ただ、値オブジェクトに慣れていない私からすると、「あれ?なんでここでローカル変数に新しい値をセットせずに、わざわざ新しいインスタンスを作るんだろう?_」と思ってしまいました。
なので、次のコードからは値オブジェクトを変更してしまう際に起きるデメリットについて書いていこうと思います。
デメリット1:複数開発で意図せぬ副作用
実際のコードを提示します。
class Money {
private $amount;
public function __construct($amount) {
$this->amount = $amount;
}
public function add($value) {
$this->amount += $value;
}
public function decrease($value) {
$this->amount -= $value;
}
public function getAmount() {
return $this->amount;
}
}
今回のコードでは、addメソッドでは新しいインスタンスを作成せずにローカル変数に値を追加するというコードにしています。
では実行メソッドを書いていきます。
//Moneyインスタンスを作成
$money = new Money(100);
//Aさんがaddメソッドを使います
$money->add(120); //この時、$money=100+120=220が入っています。
//Bさんがdecreaseメソッドを使います
$money->decrease(50); //この時、$money=220-50=170が入っています。
//Aさんがaddした値を確認します
$money->getAmount(); //220と出力されると思っていたのに170と出力されてしまう
このように同じインスタンスを使いまわしてしまった結果思っていた金額ではない数字が出力されてしまいました。
このようなことを防ぐために先程のコード実装で提示したコードを元に修正してみようと思います。
修正
class Money
{
private $amount;
public function __construct($amount)
{
$this->amount = $amount;
}
public function add($value)
{
return new Money($this->amount + $value);
}
public function decrease($value)
{
return new Money($this->amount - $value);
}
public function getAmount($value)
{
return $this->amount;
}
}
//Moneyインスタンスを作成
$money = new Money(100);
//Aさんがaddメソッドを使用
$monew_newA = $money->add(120); //$monew_newA = 100 +120 =220
//Bさんがdecreaseメソッドを使用
$money_newB = $money->decrease(50); //$money_newB = 100-50 = 50
//Aさんが出力結果を確認
$monew_newA->getAmount(); //220
このように値オブジェクトの普遍性を守ることで安心して共同開発ができるようになりました。
まとめ
値オブジェクトは不変であるという文字だけでは、なぜ?という疑問がありましたが実際のコードにしてみることで理解が深まりました。
ドメイン駆動設計についてもっと理解していきたいです。
コメント