Value Object の設計

まずは、設計・実装における Value Object を整理した方が良さそうなのでまとめてみました。
Value Object の設計方法としては、以下の3通りがあると認識しています。
# 仕事で主に使用してきた言語が C++Java なので、もし他にもあればご教示ください。

1. Singleton
インスタンスを1つしか生成しないパターンです。
Javaenum がこれに該当します。
同一性は == で判定することができます。


2. 不変オブジェクト
インスタンスが1度生成されたら、属性の変更を許可しないパターンです。
Java のプリミティブ型のラッパークラス(Integer など)、String、BigDecimal などが該当します。
Java の場合、hash と equals メソッドをオーバライドする必要があります。


3. スコープ外へ公開する際に複製する
クラスの属性であれば、他のオブジェクトに渡すとき、メソッドのローカル変数であれば戻り値で返すときなどに、インスタンスを複製します。
JavaC++ のプリミティブ型はセマンティクス的にはここに該当すると思います。
C++STL の各クラス(string、vector)などもこれに該当します。
Java の場合、hash と equals メソッドをオーバライドする必要があります。また、Cloneable を実装するか、コピーコンストラクタを定義する必要があります。
C++ の場合、コピーコンストラクタ、デストラクタ、代入演算子(=)、等価演算子(==、!=)を適切に実装する必要があります。


Java の配列や、java.util.Date、java.awt.Point などは、2 なのか 3 なのか曖昧でバグの温床になりやすいクラスなので注意が必要です。Java は比較的初心者が使うことも多く、3の方法をとった場合、複製するタイミングが難しく、言語によるサポートもないので、1または2の方法をとるのが好ましいと考えます。この辺りの話は、Effective Java の、「項目15 可変性を最小限にする」、「項目39 必要な場合には、防御的にコピーする」に詳しく書かれています。

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)

C++ は3の方法をとることが多いと思います。以前、オブジェクトの広場に記事を書いているので、よろしければご参照ください。


さて、以上のことをふまえたうえで、id:aufheben:20090430 で私が話したかったのは、次の2点です。

  • 上記の1〜3に該当しないオブジェクトを ValueValue Object と呼ぶのはやめよう。
  • Value Object として設計することが好ましい概念(それを Value と呼んでいたつもり)を定義したい。

後者はまだ自分でも考えがまとまっていません。
価値それ自身(イデア?)と、それへの参照(ラベル)を混同して議論していたような気が少ししています。


追記:
一見矛盾しているけれど、Value の本質は不変性と可変性の両方の側面があると思いました。
まず、不変性(同一性)の側面。
1という数値(1個でも1リットルでもいいですが)は、いつでもどこでも誰が見ても1であって2ではないわけです。
Aさんの体重が50kgと記録されたら、知らないところで100kgに書き換えられていたら問題です。
Value のこの特性を保証する仕組みが上で書いた1〜3の方法になります。
次に、可変性の側面。
リンゴが1個入ったかごにもう1つリンゴを加えたら、1を2に変更する必要があります。
体重も同じ。翌月測定して52kgに増えていたら、50kgを52kgに変更する必要があります。
上の方法の場合、1と2ではオブジェクトへの参照(ポインタ)を付け替えることで可変性を実現します。3の方法ではオブジェクトの属性を直接書き換えます。
ただ、くどいようですが、やっぱり Value の本質は不変性(同一性)、特にスコープ外から不用意に変更されないことかなと思いますね。そのために、不変(immutable)オブジェクトという概念を生み出したり、string の複雑な実装があるわけなので。


追記2: