テスト投稿

値オブジェクトの利点

ふるまいを持つ

お金を計算する場面を考えてみる。

お金には量と通貨がある。これを値オブジェクトとして定義すると以下のようになる。(ソースコードはLaravel)
お金は足す場面があるので、addメソッドとして定義する。

お金を表現するmoneyクラス

class Money 
{
    public function __construct(string $currency, int $amount)
    {
        $this->currency = $currency;
        $this->amount = $amount;
    }

    public function add(Money $money)
    {
        if($this->currency != $money->currency) {
            throw new \Exception('通貨が一致しません');
        }

        return new Money($this->currency, $this->amount + $money->amount);
    }
}

実際に計算を行う
class MoneyController extends Controller
{
    public function index()
    {
        $jpy1 = new Money('JPY', 100);
        $jpy2 = new Money('JPY', 200);
        $usd1 = new Money('USD', 1);

        // 日本円どうし
        $sumJpy = $jpy1->add($jpy2);

        // 別の通貨どうし 
        $sumMoney = $jpy1->add($usd1);
        
    }
}

値オブジェクトに別の通貨どうしの場合は例外を投げるように定義することで、別の通貨どうしが足されるというバグを防ぐことができる。
値オブジェクトはただのデータの入れ物ではなく、オブジェクトに対するふるまいとして自身に関するルールを持つことができる。

表現力を増す

userについての情報を持ったUserクラスを考える
苗字と名前を合わせてフルネームを取得したい場合、値オブジェクトに持たせることでフルネームを取得することができる。
「性:Tanaka 名:Taro」のようなフォーマットでフルネームを取得したい場合もFullNameメソッドを変更するだけで対応ができる。

class User 
{
    public function __construct(string $firstName, string $lastName)
    {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
        
        // 文字数チェック
        if (mb_strlen($firstName) < 3 || mb_strlen($firstName) < 3) {
            throw new \Exception('3文字以上にしてください');
        }
    }

    public function FullName() 
    {
        return $this->lastName . ' ' . $this->firstName;
    }
}
class UserController extends Controller
{
    public function index() {
        $user1 = new User('Taro', 'Tanaka');

        $FullName = $user1->FullName();
        
    }
}

不正な値を存在させない・ロジックの散在を防ぐ

先ほどのUserクラスを例に取る。
システム上では苗字・名前ともに3文字以上にしたいという場合などがある。
しかし、プログラム上では苗字・名前が2文字以下でも問題はない。
一度2文字が許容されるようのコード上で許可されてしまうと至る所で文字数チェックをする必要が出てくる。
値オブジェクトはインスタンス化する時にチェックをすることでそもそもこのような以上な値を防ぐことができる。
これは同じロジックをいたるところに書くことも防いでくれる。