はじめに

こんにちは、Sawa です。 今回は**ドメイン駆動設計(DDD)**関連の話題に触れようと思います。 ドメイン駆動設計を行う上で定義するドメインが持っているものが値オブジェクトです。 このブログを読んでいただいたということは値オブジェクトって聞いたことけどなんのこと?

という状態かと思いますので簡単に値オブジェクトについて説明していこうと思います。

値オブジェクトとは?

ざっくり言いますと、オブジェクトが持つ値(インスタンス変数)をオブジェクトとして定義したものです。

たとえば、商品を管理するシステムがあったとして「商品クラス」を定義するとします。

あなたはこの商品クラスにどのようなインスタンス変数を持たせますか?

候補として、商品名、値段、などたくさんの候補が挙げられるかと思います。

今回は商品クラスに値段というインスタンス変数を持たせるとしましょう。

おそらくこのように実装するかと思います。

public class Goods {
    
    int price;
    
    Goods(int price){
        this.price = price;
    }
    
    void setPrice(int price){
        this.price = price;
    }
    
    int getPrice(){
        return this.price;
    }
}

今回は Goods クラスを定義しましたが、値段を int で定義することは適切であるか考えてみてください。

もちろん、double や float , long で定義しましょうというようなヘンテコなことを言いたいのではありません。

懸念すべきことは int が取りうる数値の範囲が、値段が取りうる数値の範囲と一致しているかという点です。

int は 32 ビットなので -2147,483,648 ~ 2,147,483,647 の範囲まで取り扱うことが可能です。

しかし、商品の値段として負の値が入ることがありません。

もし、コンストラクタで負の値が渡されたときはどこで例外を吐かせるのでしょうか?

Goods クラスを使用するクラスで 負の値を弾くとすると、Goods クラスを生成するときにいちいち値をチェックしないといけないですよね。

こんなときに使うのが

値オブジェクト

です。

値オブジェクトについて詳しくみていきましょう。

値オブジェクトを使ってみる

さきほどは int で定義した price ですが、値オブジェクトを使うとなると Price というクラス用意してそれを Goods クラスに持たせます。

これが値オブジェクトを使った基本的な構造です。

Price クラスを Goods クラスに持たせた場合はこのようになります。

public class Goods {
    
    Price price;
    
    Goods(Price price){
        this.price = price;
    }
    
    void setPrice(Price price){
        this.price = price;
    }
    
    Price getPrice(){
        return this.price;
    }
}

これもだいぶ雑な書き方ですが、こんな感じになります。

ではこの Price クラスを定義して妥当な値段のみを入れられるようにします。

public class Price {
    
    int value;

    Price(int value) {
        if (value < 0) {
            throw new IllegalArgumentException("価格は0以上でなければなりません");
        }
        this.value = value;
    }
}

こんな感じですね。

これで Price クラスを作成する際に0以上の価格のみが設定可能になり、Price を持つ商品クラスも正しい値段を持つことができます。

このように数値などの値をオブジェクトとして表現したものが値オブジェクトです。

値オブジェクトを使えば値を持たせるタイミングで数値が正しいものが判断できます。

また、今回は値段を例に出しましたが、上限と下限がある値をもつ値をオブジェクトを定義すると、将来的に値の範囲の変化しても値オブジェクトのバリデーションの部分だけの修正でよいので保守性も高いと言えます。

これが値オブジェクトを定義するメリットです。

ただ、今のままでは Goods クラスが Price が nul の場合を許容した状態になっているので、null チェックが必要になります。

普段の業務ではアノテーションを使って. null を排除してしまいますが、形式的に null チェックを実装してみようと思います。

import java.util.Objects;
import java.util.*;

public class Goods {
    
    Price price;
    
    Goods(Price price){
        this.price = price;

        if(Objects.isNull(this.price)){
            throw new NullPointerException("値段を設定してください");
        }
    }

    void setPrice(Price price){
        this.price = price;
    }

    Price getPrice(){
        return this.price;
    }
}

実装するならこんな感じになるでしょうか?

これで値オブジェクトを持った Goods クラスを定義できました。

今回は細かい定義を無視して値オブジェクトについて説明しました。

値オブジェクトが何者なのかについてざっくりとしたイメージを掴んでいただけたら幸いです。

投稿者 Sawa