続・クラス図でモデリングしても実装に落とせないのは何でだ


いただいたコメントをきっかけにしてさらに考えてみました。

出荷済みの受注明細に載っている商品の単価は受注時点の単価なので、受注明細にも単価を持っているのでは?

自分が何を言いたいのかまだよくわかんないのですが、がんばって書いてみます。

クラス図の痛し痒し

単価というデータの置き場所がどこであるべきかという話であれば、おっしゃる通り、受注明細に置けばいいだけの話であります。
データモデリングの世界ならそれで終わりです。
オブジェクト指向の世界でも、「受注伝票というモノをオブジェクトにするんだ」と考える人なら、受注明細オブジェクトに単価を持たせるかもしれません*1
この場合も、ここで話は終わりです。


一方で、オブジェクト指向の世界には、違うモノの見方をする人が居ます。
商品の単価は商品クラスが知っているべきだ、と考える流派です。
図を引用させていただいた本の人もそうだし、Hibernate の Gavin King(ガビーン王)なんかもそういう考えだと思います。


この考え方で描いたクラス図からは、商品オブジェクトが抱えている単価データをどこから持ってきたのかが分かりません。問題領域の構造(この場合は単価の管理方法)が見えない。だから実装に落とせない。


逆にクラス図で問題領域を正確に描こうとすると、「クラス図には描くけど実装しないクラス」という妙なものができてしまい、やっぱり実装に落とせない。
例えば、商品クラスに商品単価クラスのリストを持たせたら、「受注日時点の単価は商品単価クラスが持っているのだな」ということ(=問題領域の構造)が分かります。

+--------------+         +--------------+         
|     商品     |         |   商品単価   |         
+--------------+         +--------------+         
| 商品コード   |1     1..| 商品コード   |          
| 商品名       +---------+ 単価         |
|              |         | 適用開始日   |
|              |         | 適用終了日   |         
+--------------+         +--------------+         

でも、こんなクラス実装しないでしょう。実装時は、商品のインスタンスに適切な単価をロードして、get単価メソッドは単純にそれを返すだけにするでしょう。

クラス図を描いても大事なことが決まっていない気がする

データモデリングすると、そのシステムが出来ること・出来ないことの境界が決まってしまいます。
例えば:

  • 単価が商品マスタにしかなかったら、単価を更新すると過去の単価が失われることが決まる
  • 受注明細に単価を追加すれば、売上時点の単価を取得できるようになるが、売上のなかった日の単価は相変わらず分からないことが決まる
  • 商品マスタに { 商品ID, 単価, 適用開始日, 適用終了日 } からなる商品単価マスタを外付けしたら、任意の時点の単価が取得できることが決まる

どんな天才がプログラムを書いても、この境界を超えることはできません。
一方クラス図では、そのシステムでできること・できないことの境界は決まりません。永続化層の作り込み次第でどうにでもなってしまう。
私がクラス図でモデリングしてみても何か頼りない気分になるのは、この実装時点での自由度の高さに原因がありそうです。

まとめ

自分で書いてることが何か腑に落ちない。
この話は仕切り直しだ。

*1:さらに商品クラスの get単価 メソッドを、所属する受注明細オブジェクトの get単価 メソッドに delegate するように実装するのかもしれません