ValueObject
ValueObjectはドメイン駆動設計という設計手法の1つで値オブジェクトとも言われる設計パターン。
Domain-Driven DesignのことでDDDと略されることもある。
有名な本では「実践ドメイン駆動設計」で、IDDD本(Implementing Domain-driven Design)とも呼ばれている。
特徴
1.計測・定量化・説明の責務をもたせる
2.イミュータブルオブジェクト
3.概念を統一させる
4.交換ができる
5.一意性を持たせず等価性をもたせる
6.副作用のないふるまいをさせる
1.計測・定量化・説明の責務をもたせる
1つのオブジェクトには1つの意味を持たせ、その意味に即した処理をオブジェクト内に実装する。
「値段」というオブジェクトであれば「その値段に対し計算する値」とか。
- 値段に別の値段を合算する処理
- 値段から一定料金引き算する処理
- 値段に消費税や割引などを計算する処理
- 「料金は**円」と返却する処理
みたいな感じ
このときオブジェクトの値を変更するのではなく、引数に対しオブジェクトの値を計算した値を返却するようにする。
値を変更しない=イミュータブルであることは次項目にて
2.イミュータブルオブジェクト
イミュータブル=値が変更されないもの。読み取り専用、readonly、getterのみてきな。
ミュータブル=値が変更されるもの。読み書き可能、setter、getter両方あるてきな。
オブジェクト生成時に設定した値から変更を不可とする。
オブジェクトに対して不変性をもたせることができる。
メリットは
- 呼び出すタイミングがどこであれ中身が同じであるため使用者が混乱しない。
- 値が変わらないのでキャッシュが可能
などなど。
3.概念を統一させる
値オブジェクトは複数の属性(プロパティ)を持てて、相互に関係している。
先ほどの「値段」であれば、値段そのものを表す数字(100円であれば100など)部分と、通過の単位(100円であれば円など)部分といったもの。
数字だけでは「値段」としての意味を持たないが、そこに円やドルなど通貨の単位を持たせることで「値段」という意味をもたせることができる。
複数の組み合わさった属性によってオブジェクトを適切に説明できることを、「概念的な統一体」と呼ぶ。
4.交換ができる
値オブジェクトは、生成時に設定した値から変更できないイミュータブルなオブジェクトである、とは上記でやった通り。
実際のプログラムでは途中で変更を行いたい時がある。
例えば「日本円で設定したけどドルで設定し直したい」など。
値オブジェクトは一意の値…idなど生成したオブジェクトにユニークな属性を持たせないものなので、別の値を設定したい場合は再生成して交換する。
price というオブジェクトを生成したあとに中身を変えたいときは、new_priceなどといったオブジェクトを生成したあとにpriceに代入する。と解釈している。
長期にわたって識別する必要があるオブジェクトであればエンティティとよばれるオブジェクトで管理することもできる。が、生成して代入が面倒だからとエンティティのやり方をするのはよろしくない。
エンティティについてはこちら実践DDD本 第5章「エンティティ」 ~一意な識別子で同一性を識別~
5.一意性を持たせず等価性をもたせる
一意の値を持っているオブジェクトであれば、その属性で比較ができた。
値オブジェクトの場合、一意の値を持たないので「各属性の値が全て一致するか」で判定できる。
userA.id == userB.id
ではなく
priceA == priceB
ができる。
6.副作用のないふるまいをさせる
生成した段階でセットした値から変わらないイミュータブルなオブジェクトなので、どのタイミングで呼び出しても中身が変わることはない。
それはつまりそのメソッドが他の処理に影響を及ぼすことがないことでもある。
3000円の値段に対し消費税を足した値段を取り出したいから1.1乗算しようとしたら、実は30ドルになっていて3300円がほしいのに33ドルが返ってきたら、その部分の処理に影響が出てしまう。
しかしセッターのような処理はなく生成時点で設定した値から変わることはないので、どこで呼び出しても必ず同じ値が返ってくる。
こういった他の処理に影響がないことを「副作用がない振る舞い」と呼ぶ。