Dependency Injection
「DI パターン」と これを利用した「DI コンテナ」ってのがある
「DI コンテナ」使いたいけど「DI パターン」って何?状態 > 荒いけどまとめる
ここにあることが全部ではないので、正確なことは最下記リンクへ
DI パターンとは何か
「インスタンス変数にオブジェクトを割当てること」
「或るクラスに依存するオブジェクトの作成を、他の誰かの責務にすること」
James Shore 曰く
「大げさな言い回しだ(良い意味でと付け加えておく)」
まず DI パターンが出来るまでの実情
概要
- 一般的に或るクラスは 多数の他クラスオブジェクト (以下 "dependencies")
を属性として持っている - "dependencies" は要求/仕様変更/テスト実行時に
メンテナンス、テストによってはスタブ/モックの用意が必要- ...この「メンテナンス、スタブ/モック用意」
やり易い方法がないものか?
- ...この「メンテナンス、スタブ/モック用意」
詳細(例を交え)
- 下サンプルの SmileCar について、 Wheel/Engine の "dependencies" は vendorABC である
- もし、"dependencies" が vendorABC ではなく vendorXYZ になった場合
- "dependencies" をそれぞれ vendorABCWheel(), vendorABCEngine() を変更する
- ... が、これって本当に良いやり方?
- メンテは面倒(影響範囲調査など、巨大クラスになれば尚更)
- 責任範囲という面でも SmileCar(車自身) が
"dependencies"(Wheel(タイヤ) や Engine(エンジン)) の「生成」にまで
責務を持つべき?
- ... が、これって本当に良いやり方?
(before)
(Whee/Engine がそれぞれ 抽象クラスを具象化したクラスであったり
interface を implements している前提をかなりすっ飛ばしているのは意図的)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorABCWheel(); // 仕様変更でメンテが必要になりそう private Engine engine = new vendorABCEngine(); // 同上 public Car() {} public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
(after)
public class SmileCar { private DatabaseUtil db; private Wheel wheel = new vendorXYZheel(); // vendorABC > vendorXYZ private Engine engine = new vendorXYZEngine(); // 同上 public Car() { db = new DatabaseBase(); } public showWheelNameAndEngineName() { try { // テストの時はどこの DB 見にいく? // getConnection() 内で 本番の時は... テストの時は... // って 分岐してないですよね? db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI パターンの登場
やることは単純に、冒頭通り
「インスタンス変数にオブジェクトを割当てること」
上の SmileCar (仕様変更後) を例にする
インスタンス変数にオブジェクトを割当てる
まずは Wheel/Engine オブジェクトをインスタンス変数に割当てる
これで、Wheel/Engine の仕様変更があっても「SmileCar クラス自身」は手をつけなくていい
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(Wheel wheel, Engine engine) { db = new DatabaseBase(); this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
次に db オブジェクトをインスタンス変数に割当てる
クラス設計がいけてないので「え?」となるが、あくまで一例
public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
DI 自体は終わったが、データベースモック化はどんな風に?
概略コードは以下
- SmileCar は何もしなくて良い
- 本番DB に繋ぐかモックしたものを作るかは MockDatabase ないの処理で決める
public MockDatabase extends DatabaseUtil { // モック用のフィールド、メソッド用意 } public class SmilCarTest { public void TestShowWheelNameAndEngineName() { MockDatabase mockDatabase = new MockDatabase() SmileCar smileCar = new SmileCar( mockDatabase, new vendorXYZWheel(), new venXYZEngine() ); smileCar.showWheelNameAndEngineName(); smileCar.sometest(); } } public class SmileCar { private DatabaseUtil db; private Wheel wheel; private Engine engine; public Car(DatabaseUtil db, Wheel wheel, Engine engine) { this.db = db; this.wheel = wheel; this.engine = engine; } public showWheelNameAndEngineName() { try { // (特に指定がなければ) 本番用の DB を使う // テスト用の DB(モック) を使う > SmileCarTest#TestShowWheelNameAndEngineName db.getConnection(); System.out.println(wheel.getWheelName()); System.out.println(engine.getEngineName()); } catch(Exception e) { e.printStacTrace(); } } }
「依存性の注入」について思うこと(感想レベル)
DI[Dependency Injection] が 「依存性の注入」と訳されている
少しでも英語リソースやそこにある議論を眺めたら "dependency"
が「依存」そのものじゃなくて
「オブジェクト」やこれに近しい「モノそれ自身」だと気づくはず
引用参考リンク
http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
http://stackoverflow.com/questions/130794/what-is-dependency-injection?rq=1