クラス設計のとっ散らかりをなんとかしたい(インスタンス生成)
とっ散らかりポイント
インスタンス生成
DTOなんかのデータ保持
ロジックの責任
別システムとのI/O
これらのそれぞれ、もしくは全部が一つのクラスの一つのメソッドにあったりするととっ散らかるわけですね。
で、上記のポイントについてとっ散らからない方法があってそれを愚直に当てはめていくだけで単一責任の原則にのっとったクラス設計に近づけるかなという思考実験。
そのうちのインスタンス生成について考えてみます。
インスタンス生成してその場で利用する
どういうこと?
あるクラス内で利用するクラスのインスタンス生成を利用するメソッド内で行ってしまうこと。
どういうデメリットがあるかというと実装との依存関係が切り離せなくなるのでモックオブジェクトによるテストが困難になってしまう。
メリットは一応実装が何か分かりやすいという人もいるけど、それってそんなに大事かね?
解決方法
これはもう、DIに任せること。DI Containerを利用していない場合はインスタンスを生成する箇所と使う箇所を分けることができる。*1
具体例
いかんやつ
public DataDtoCreate() { var logic = new DataCreateLogic(); return logic.Create(); }
OKなやつ
private IDataCreateLogic _logic; public void Initilize() { _logic = new DataCreateLogic(); } public DataDto Create() { return _logic.Create(); }
いかんやつの方
この場合、DataDtoCreateメソッドのユニットテストをしたい場合は、DataCreateLogicとの依存関係が切り離せず、呼び先であるDataCreateLogicを踏まえたテストをせざるをえません。
ユニットテストなのに複数のクラスの責任にまたがるテストをするってなんか変だし、そのテストケースの実装も大変なことになりがち。
OKなやつの方
_logicのインスタンス生成と使用する箇所が異なるので、(interface等で抽象化されていれば)mockに差し替えることができるのでユニットテストをするうえでテスト対象の事象にスコープを限定することができます。
という話を、プロジェクトメンバーにいつもするんだけどうまく説明できないのでこうのような考察をしてみました。
もう少しすっきりまとまった話になれば分かりやすくなるかな?。
でも、今日はここまで。
*1:ただし、DI Container自体はCoponentの寿命をこんとろーるする責任をFrame Workに任せられるというメリットが主で依存性の注入はその副次的な要素だという話も聞く。