DDD / Assertions


表明-ASSERTIONS (p.255)

要約

表明-ASSERTIONS
  • 複雑な計算を副作用のない関数に分離することで問題は小さくなるが、依然として副作用を生み出すようなEntityは残っており、それらを使う開発者は操作の結果を理解する必要がある。ASSERTIONSは副作用を明示し、扱いやすくする。
    ***

問題

  • より高位の命令を使う開発者が、下位の操作の結果を理解しなくてはならなくなると、カプセル化はあきらめるしかなくなる。インタフェースでは副作用を制限することができないため、良い解決策にはならない。抽象化やポリモーフィズムもあきらめなくてはならなくなる。

問題サマリ

  • 操作の副作用が、実装によって(暗黙的に)しか定義されていない時に委譲を多用すると、原因と結果がこんがらがった設計になる。
  • プログラムを理解するには、分岐パスを辿ってプログラム実行をトレースするしかなくなる。こうなると、カプセル化の価値は失われ、抽象は壊れてしまう。

解決

  • 内部を詳細に調べることなく、設計の意図や操作の結果を知るための方法が必要。意図の明確なインタフェースはこのうちの一部を担ってくれるが、簡単な形式での意図の提示では十分でない場合がある。
  • DbCはクラスやメソッドに対して"表明(Assertions)"を定義することで、開発者が「真」であると保証する内容を表現している。要約すると…
    • 事後条件は、操作が引き起こす副作用と、メソッド呼び出しの(保証された)結果を記述する。
    • 事前条件は詳細な契約書のようなもので、事後条件が保証されるために満たさなければならない条件を記述する。
    • クラス不変表明は、すべての操作の終了時にオブジェクトが満たす状態を記述する。
    • 不変表明は、AGGREGATES全体に対して宣言することもでき、一貫性に関する規則を正確に定義できる。
  • これらすべての表明は「手順」ではなく「状態」を記述するため、分析が簡単になる。
    • クラス不変表明は、クラスの意味を明らかにするのに役立ち、オブジェクトの振る舞いを予測しやすくすることで、クライアント開発者の仕事を簡単にする。
    • 委譲の結果は表明として組み込まれるため、事後条件による保証さえ信用できれば、内部を気にする必要はない。

解決方法サマリ Therefore:

  • 操作の事後条件とクラスやAGGREGATEの不変表明を記述しろ。
  • 使っているプログラミング言語で直接表明を記述できないなら、自動化されたユニットテストを書け。
  • 表明を、プロジェクトの開発プロセスに合致するような場所(ドキュメントやダイアグラム)に記述しろ。
  • 概念のセットに合うようなモデルを探せ。そうるすことで開発者は表明の意図を推測しやすくなり、学習効率は上がり、矛盾したコードを書くリスクが減る。
  • 自動化されたユニットテストは、言語サポートの欠如を部分的に補う。表明は「手順」ではなく「状態」なので、テストを書きやすくもしてくれる。
    • setupは事前条件を整えることに対応する
    • 実行後のチェックは、事後条件が満たされているかの確認に対応する
  • 事前/事後条件および不変表明を明示的に示すことで、開発者は操作やオブジェクトを使った際の結果を理解することができるようになる。
  • 理論的には、無矛盾な表明のセットであればどんなものでもうまくいくが、人間が理解できるようにすることを忘れてはならない。
    • 人間は述語をコンパイルできない。
    • 概念の不足する部分を補ったり、未知の概念については既知の概念から推測したりできる。
  • アプリケーションのニーズを満たすのと同様に、人間にとって意味のあるモデルを見つけることが重要となる。
***

例:Back To Print Mixing

  • 先ほどの例では、mixInメソッドが呼び出された場合に引数に何が起きるかが曖昧になっている点を気にしていた。
  • レシーバのvolumeは引数のvolume分増える。物理的な絵の具に関する理解からすれば、渡された絵の具の量はゼロになるか、完全になくなるはずだが、現在の実装ではそうなっていない。引数の変化はリスクのある副作用でもある。
  • まずは足元を固めるために、mixInの事後条件を書いてみよう。
    • After p1.mixIn(p2)
      • p1.volume is increased by amount of p2.volume
      • p2.volume is unchanged
  • これは直感的でない(現実と異なっている)ため、開発者が間違えやすい。
  • 直接的な解決策は、引数のvolumeをゼロにすること。引数の変更は悪いプラクティスだが、直感的にわかりやすい。
  • が、元々の設計には意図があることが判明する。
    • 最後に「追加された混ぜ合わされていない絵の具」の一覧を出すらしく、どの絵の具が混ぜられたかわかるようにしなくてはいけなかった。
  • 論理的に一貫させると、アプリケーションの要求に合わないケースと言える。このようなケースではどうすれば良い?奇妙な事後条件をコミュニケーションで補っていくしかないのか?
    • この世界はすべてが直感的なわけではないが、時にはベストアンサーが存在する。このケースでは、欠けている概念を示唆している。
  • まずは、Paintが2つの責務を持っているように見えたので、分割。
    • 命令は「mixIn」のみになった。(Figure10.10)
  • なんかうまくいった。(以下略)
***
  • 副作用のない関数や表明による予測可能性や意図の明確なインタフェースによるコミュニケーションのしやすさは、カプセル化や抽象をより安全にしてくれる。
  • 要素を再結合可能にするために、次に紹介するのは「効率的な分解」について...

担当者のつぶやき

みんなの突っ込み

  • commons-langにValidateっていうクラスがありますよね。皆さん、どのくらい使われてますか? -- 佐藤? 2008-12-21 (日) 10:56:01

まとめ (議事録)