顧客一覧などは通常、ドメインオブジェクトであるCustomerのListとして記述されることが多いかと思います。
しかし、プリミティブなList型だと外部から要素の追加や削除などが可能になります。
これは内部状態の変更にあたり、プログラムの理解を難しくします。
ファーストクラスコレクションとして扱う
そこで、コレクションもドメインオブジェクトと捉えます。
List<Customer>として扱うのではなく、独自に定義したCustomerListクラスとして扱います。
このような配列やコレクション型のドメインモデルのことを、ファーストクラスコレクションとかコレクションクラスと呼びます。
ファーストクラスコレクションの基本
CustomerListはインスタンス変数としてList<Customer>を持ちます。
そして、その変数を操作する、add()やremove()などを持ちます。
こうすることで例えばadd()する際の業務ルールをカスタマイズすることができます。
プリミティブなList型の場合は、単純に要素を追加するだけです。
一方、ファーストクラスコレクションを用いれば、例えば「重複はx件まで許可する」などの業務ルールを表現することができます。
ファーストクラスコレクションを用いなくても、呼び出し側のロジックで表現することは可能です。
しかし、ファーストクラスコレクションを用いることで、データを操作をひとまとめにでき、業務ルールの散財を防げます。
内部の値は変更しない
また、値オブジェクトと同様に内部の値の変更は許可しません。
内部の値を変更する場合は、配列を別オブジェクトとして返します。
こうすることで変更前と変更後の配列は別のオブジェクトとなり、内部の状態を理解しやすくなります。
// NGなadd()メソッド public List<T> add(T elem) { // インスタンス変数を変更してしまっている return this.list.add(elem); }
// OKなadd()メソッド public List<T> add(T elem) { // 変更前と変更後のListが別オブジェクトになっている List<T> result = new ArrayList<>(this.list); return result.add(elem); }
インスタンス変数を返す場合は変更不可にして返す
Listの参照をそのまま返すようなgetメソッドは作成しません。
どうしても必要な場合は、変更不可能な形にして渡します。
return Collections.umoodifiableList(List<T>);
これにより、Listの要素の追加、削除や順番の並べ替えは防ぐことができます。
しかし、個々の要素の変更は防ぐことができません。
ですが、これは個々の要素が値オブジェクトとして定義されていれば防ぐことができます。
ファーストクラスコレクションを作成するメリット
抽象的な話だけだといまいちファーストクラスコレクションを作成する意味が分からないかもしれません。
ファーストクラスコレクションは、複雑な業務ルールがいくつも存在するようなドメインに対して効果を発揮します。
例えば、顧客リストの要件として、同じ名前の顧客でも枝番アカウントなどで3つまで重複可能などの要件があったとします。
その場合、重複が3つ以内かどうかを確認するロジックが必要になります。
プリミティブなList型だと、呼び出し側でその制御を行う必要があります。
その後、この要件が「10件まで重複を許可する」と変更されたとします。
その場合、呼び出し側の3件を全て10件に変更しないといけません。
このような要件が数十や数百あった場合、そのルールが呼び出し側に散在するのは、良い設計とは言えません。
ファーストクラスコレクションにまとまっていた方が、プログラムの見通しは良くなり、変更も容易になります。
端的に言えば、ファーストクラスコレクションの利点は以下の二つです。
・内部状態の変更を防ぐ
・業務ルールの散在を防ぐ