対象読者層
- クラス設計に悩んでいる人
- 値オブジェクトの概要を理解したい人
値をクラス(型)として表現する設計パターンのこと
値オブジェクト(Value Object)は、オブジェクト指向設計におけるクラス設計パターンの一つです。
具体的には、システム内で登場する値をクラスとして表現します。
本・DVDのレンタル管理システムを例にしてみましょう。
レンタル管理システムには、レンタル開始日、レンタル終了日という値を扱います。
これをそのままコードに書くと、このようになります。
Calendar rentalStartDate;
Calendar rentalEndDate;
上記のコードは、Calender型で、「レンタル開始日・レンタル終了日」と定義しています。
値オブジェクトで設計すると、コードはこのようになります。
RentalStartDate rentalStartDate;
RentalEndDate rentalEndDate;
レンタル開始日・レンタル終了日を表現する型(クラス)を作りました。
このように、システム内で登場する値を型(クラス)で表現する設計パターンが値オブジェクトです。
値オブジェクトで設計するメリット
値オブジェクトで設計するメリットは大きく3つあります
- その値に関連するメソッドをクラスに集約しやすい
- 値の受け渡し間違いを防げる
- 簡単に導入できてメリットが大きい
値オブジェクトで設計すると、値に関連するメソッドを1つのクラスに集約しやすくなります。
例えば、ECサイトを構築しており、その中で送料という値を値オブジェクトで設計したとします。
/**
* 送料クラス
*/
public class Postage {
private int mPostage = 0;
public Postage(int postage) {
mPostage = postage;
}
public int getValue() {
return mPostage;
}
}
ECサイトで注文する際には、送料が発生します。
この送料を計算する処理(メソッド)を追加する場合、送料が表現されたPostageクラスに追加することで送料に関連するメソッド(処理)が集まりやすくなります。
/**
* 送料クラス
*/
public class Postage {
private int mPostage = 0;
public Postage(int postage) {
mPstage = postage;
}
public int getValue() {
return mPostage;
}
/**
* 配送先への送料を計算します
* @param address 配送先
* @return 送料
*/
public static Postage calculate(String address){
// 具体的な処理は省略
return new Postage(result)
}
}
また、値を型として表現できるため意図しない値の混入を防ぐ事ができます。
例として、先ほど追加したcalculateメソッドを見てみましょう。
calculateメソッドの引数である住所は、String型で定義されています。
/**
* 配送先への送料を計算します
* @param address 配送先
* @return 送料
*/
public static Postage calculate(String address){
// 具体的な処理は省略
return new Postage(result)
}
このメソッドが想定している住所の値は、「東京都足立区~」のような文字データを想定しています。
しかし、String型で定義されているため、住所とは関係ない文字列も渡せてしまいます。
そこで、住所という値を値オブジェクトで設計したコードに修正します。
/**
* 配送先への送料を計算します
* @param address 配送先
* @return 送料
*/
public static Postage calculate(Address address){
// Addressクラスのコードは省略
// 具体的な処理は省略
return new Postage(result)
}
String型だった住所をAddress型(クラス)として定義することで、住所に関連するデータのみ渡せるように制限しました。
これにより、誤ったデータを渡してしまうリスクを防ぐことができます。
更に、calculateメソッド側での引数チェックは楽になり、シンプルなメソッドにすることができます。
値オブジェクトの魅力は、簡単に導入でき、メリットが大きい点です。
サンプルコードからもわかるように、値をクラスとして表現するだけなので簡単に導入できます。
更に、上記のメリットはコードの保守性・可読性の点において恩恵がとても大きいです。
システム内で重要な意味を持つ値に絞って適用する
システム内で使われるすべての値を、値オブジェクトで設計する必要はありません。
過度にやりすぎると、コードが複雑になる可能性があるためです。
以下のような項目は必ずしも最適ではありません。
-
単純な文字列や数字で、特別な制約やルールがない項目
例:メモ欄、説明文、タグ -
システム内部でのみ使用される技術的な値
例:内部ID、フラグ、ステータスコード
重要な値に絞って値オブジェクトを適用しましょう。
さいごに
値オブジェクトについて簡単に解説してみました。
実は、より値オブジェクトを安全に設計するためにはもう一つ重要なテクニックがあります。
ここで書くと長くなってしまうため、こちらの記事で解説しています。
ここまで読んでいただきありがとうございました!