値オブジェクトはエリック・エヴァンスのドメイン駆動設計 | Eric Evans, 和智右桂, 牧野祐子, 今関剛 | 工学 | Kindleストア | Amazonの中で次の様に述べられています。
あるオブジェクトが、ドメインにおける記述的な側面を表現し、概念的な同一性を持たない場合、そういうオブジェクトは、値オブジェクトと呼ばれる。値オブジェクトがインスタンス化される際に表現しようとするのは、何であるかだけが問題となり、誰であるか、あるいはどれであるかは問われないような設計の要素である。
Eric Evans. エリック・エヴァンスのドメイン駆動設計 (Kindle の位置No.2480-2483). 翔泳社. Kindle 版.
乱暴な言い方をすれば任意の型で定義されたインスタンスを実現するオブジェクトです。例えば、PHPならば次のような実装ができます。
class GeoPoint
{
private $lat;// 緯度
private $lng;// 経度
/**
* GeoPoint constructor.
* @param $lat
* @param $lng
*/
public function __construct($lat, $lng)
{
$this->setLat($lat);
$this->setLng($lng);
}
/**
* privateプロパティを読み取り可能にする.
* @param $name
* @return mixed
*/
public function __get($name)
{
return $this->$name;
}
/**
* 一部のprivateプロパティを書き込み可能にする.
* @param $name
* @param $value
* @return mixed
*/
public function __set($name, $value)
{
$setter = 'set'.ucfirst(camel_case($name));
return $this->$setter($value);
}
/**
* privateプロパティにissetできるようにする.
* @param $name
* @return bool
*/
public function __isset($name)
{
return isset($this->$name);
}
/**
* 存在しないメソッドが呼ばれたらnullを返す.
* @param $methodName
* @param $arguments
* @return null
*/
public function __call($methodName, $arguments)
{
return null;
}
/**
* __setで呼ばれるprivateプロパティへのsetter
* @param $value
*/
private function setLat($value)
{
if (-90 < $value && $value < 90) {
$this->lat = $value;
}
}
/**
* __setで呼ばれるprivateプロパティへのsetter
* @param $value
*/
private function setLng($value)
{
if (-180 < $value && $value < 180) {
$this->lng = $value;
}
}
}
上コードならば緯度経度で地点を表現するオブジェクトです。インスタンス化時、プロパティ変更時に必ずsetHoge()を通し、setHoge()の中で不適正な値が代入されることを防ぎます。例ならば緯度は必ず-90度から90度の範囲であるし、経度は-180度から180度の範囲と限っています。setに失敗したら何も起きない作りですが、もっと厳格に作って、失敗したら即座に例外をthrowするのも全然ありです。
値オブジェクトは極端なカプセル化の実現です。上記コードの様に好き勝手ルールを増やしても外からはルールは変えられず、見えません。必要なプロパティだけが見えます(キャストとか便利コンストラクタとかもありな気がします)。
上記の例は緯度経度のみのざっとした作りですが、プログラムで扱う領域に複雑な概念や特殊な値の振る舞いがある程、値オブジェクトは強力になります。
値オブジェクトを導入していないコードで値オブジェクトを導入した方が見通しがよくなる時、よくならない時があります。よくならない時は単純な種類のデータを扱っている時です。上の例にある通り、ちょっとしたものでも数十行コードが増えます。増えた分読みにくくなります。よくなる時は色々ですが、同じキーセットの配列やオブジェクトが至る所で現れるコードはよくなりやすいです。そういった時は特定のキー名で同じ概念を表現していることが多いです($hoge[‘lat’], $hoge[‘lng’]で検索すると恐ろしい数が出たり)。そういったキーをそのままプロパティにしてプロパティに適したルールをつけるだけで安全で読みやすいコードになります。