DIまわりについて(1)
1年前くらいから、世の中の流れに従う形でDIコンテナ(Spring)を使用した開発を行っていました。極最近は、wicketやGuiceの良さが話題になっていて、新しい流れが出始めているようです。
ただ、「Spring,S2がいい」「今度はGuiceだ」と世の中の流れに沿っているだけでは自分の進歩がないので、背景を整理する必要があるはずです。
テストしにくいクラス
DIを使わない開発をする場合、やはりテストしにくいクラスを作ってしまいがちです。要するに、テスト対象が使用するインスタンスをテスト対象のクラスで気楽に生成してしまうため、テストで検証したいロジックを局所化できないという状況になり易いです。
これは、DIコンテナを使用することで解決してきたことです。
テストとは
オブジェクト指向でのテストとは、インスタンスがある状態にあるときに、メソッドを実行したら、その後期待した状態になるかどうかを確認することです。メソッドの仕事は前提条件の元で何らかの処理を行い事後条件を得ることであり、テストとは前提条件と事後条件を比較することです。なので前提条件と事後条件を設定したり参照したりできる必要があるはずです。
- 前提条件
- インスタンスの状態
- private定義のインスタンス変数の状態は、設定できません。→問題
- 引数
- 引数は、テスト時に指定できるので、設定可能です。
- インスタンスの状態
- メソッドのロジック
- 前提条件に対して何らかの処理を行う。
- 事後条件
- 戻り値
- 戻り値は、テスト時に取得できるので、参照可能です。
- インスタンスの状態
- private定義のインスタンス変数の状態は、参照できません。→問題
- 戻り値
テストを難しくしていること
メソッドのロジックのテストを難しくしているのは、インスタンス変数の状態を操作できないためということが言えます。
解決策
テスト時に、前提条件となるインスタンスの状態を指定できれば、メソッドのロジックをテストし易くなります。
DIコンテナにinjectしてもらうことを前提とすることで、テストの前提条件を設定(mockに置き換えたり)できるようにしテストをし易くする。テスト対象のクラスからデータ依存(インスタンス変数や使用しているクラス)を排除して、テスト対象を純粋なロジックに限定するのに役立っている。
ネガティブに考えると、カプセル化を崩して、副作用のない構造化プログラミング 副作用のない関数にしてるって言えるのか?
- 3/19追記:「副作用のない関数」の方が雰囲気的に正しそうなので修正。
密接な依存関係を持つ実装クラス
なんだっけ?
以前は、S2界隈のブログを見て理解したつもりになってたのですが、今ちゃんと説明できません。(??)
依存関係が引き起こす問題
各クラスが強い依存関係があると、修正を行うときのリスクや影響範囲が特定しにくくなるのでよくない。
原因
使うクラスをnewしてることにより、使うクラスの実装依存が高くなる。または、単純に設計に失敗していることも考えられる。
解決策
使うクラスをnewしちゃって結合が強くなる問題は、インスタンスの生成は使用する部分と切り離して、使う側ではinterfaceとして使用することで、依存関係が密結合になるのを防ぎます。具体的な解決方法として、インスタンス生成を別のところ(DIコンテナ)にしてもらいます。
DIしておけば、実装クラスの切り替えが簡単というのが売り文句だった気がしますが、実装クラスを切り替えたことはありません。
厳密にはinterface経由にすることで疎結合が実現できるわけではないはずです。設計に失敗していれば、それは設計からやり直しか?
まとめ
なんだか、まともに理解できていないし、所々間違っていそうな自分に気づいただけでも収穫かな。
つづく。