DI(依存性注入)について

642 views

Published on

DI(依存性注入)について

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
642
On SlideShare
0
From Embeds
0
Number of Embeds
22
Actions
Shares
0
Downloads
1
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

DI(依存性注入)について

  1. 1. DI(依存性注入)について 伊藤 結
  2. 2. ところで、DIと聞いて   ピンとくる方はいますか?
  3. 3. DI(依存性注入)とは なるほど。わからん。 依存性の注入(英: Dependency injection)とは、コンポーネント間の依存関係をプロ グラムのソースコードから 排除し、外部の設定ファイルなどで注入できるようにするソ フトウェアパターンである。英語の頭文字からDIと略される。 wikipediaより https://ja.wikipedia.org/wiki/依存性の注入
  4. 4. DI(依存性注入)とは 依存性の注入(英: Dependency injection)とは、コンポーネント間の依存関係をプロ グラムのソースコードから 排除し、外部の設定ファイルなどで注入できるようにするソ フトウェアパターンである。英語の頭文字からDIと略される。 wikipediaより https://ja.wikipedia.org/wiki/依存性の注入 言葉の意味から考えてみよう!
  5. 5. 依存性ってなに?
  6. 6. public class Siphon implements BrewingMethod { @Override public String brew() { return "サイフォンでいれたコーヒー "; } } public class CoffeeShop { public String brewCoffee() { BrewingMethod siphone = new Siphone(); return siphone.brew() + "が出来上がりました [_]P"; } } public class CoffeeShopApp { public static void main(String... args) { CoffeeShop coffeeShop = new CoffeeShopApp(); System.out.println(coffeeShop.brewCoffee()); } }
  7. 7. public class Siphon implements BrewingMethod { @Override public String brew() { return "サイフォンでいれたコーヒー "; } } public class CoffeeShop { public String brewCoffee() { BrewingMethod siphone = new Siphone(); return siphone.brew() + "が出来上がりました [_]P"; } } public class CoffeeShopApp { public static void main(String... args) { CoffeeShop coffeeShop = new CoffeeShopApp(); System.out.println(coffeeShop.brewCoffee()); } } プログラムの実行クラス BrewingMethod(抽出方法) の実装クラス
  8. 8. クラスAからクラスBを直接インスタンス化している時、 クラスAはクラスBがないと動きません。 このような状態は クラスAとクラスBが依存関係にある、 クラスAはクラスBに依存しているといえます。 今回の例でいうと、  CfeeShopAppとCoffeeShop  CoffeeShopとSiphone の間で依存関係があるということになります
  9. 9. 注入ってなに?
  10. 10. 実際のコードで説明します
  11. 11. DIコンテナを使って、   実際にDIをやってみよう!
  12. 12. DIコンテナとは ● すごく簡単に言うと、「XXにこのクラスのオブジェクトを注入してね」 という設定を書いておくとDI(依存性の注入)を実行するよくんのこと。 今回はJava向けのDaggerというDIコンテナを使います。
  13. 13. @Module(injects = CoffeeShopApp.class) public class BrewingMethodModule { @Provides public BrewingMethod provideBrewingMethod() { return new Siphon(); } } public class CoffeeShop { // Siphonオブジェクトが注入される @Inject BrewingMethod brewingMethod; .... } public class CoffeeShopApp {   //兄弟クラスがないので設定なしでCoffeeShopオブジェクトが注入される @Inject CoffeeShop coffeeShop; public void run() { System.out.println(coffeeShop.brewCoffee()); } public static void main(String... args) {     // Daggerのオブジェクト生成(設定を読み込んで、各オブジェクトが注入されたCoffeeShopAppオブジェクトを生成) ObjectGraph objectGraph = ObjectGraph.create(new BrewingMethodModule()); CoffeeShopApp coffeeShopApp = objectGraph.get(CoffeeShopApp.class); coffeeShopApp.run(); }
  14. 14. @Module(injects = CoffeeShopApp.class) public class BrewingMethodModule { @Provides public BrewingMethod provideBrewingMethod() { return new Siphon(); } } public class CoffeeShop { // Siphonオブジェクトが注入される @Inject BrewingMethod brewingMethod; .... } public class CoffeeShopApp {   //兄弟クラスがないので設定なしでCoffeeShopオブジェクトが注入される @Inject CoffeeShop coffeeShop; public void run() { System.out.println(coffeeShop.brewCoffee()); } public static void main(String... args) {     // Daggerのオブジェクト生成(設定を読み込んで、各オブジェクトが注入されたCoffeeShopAppオブジェクトを生成) ObjectGraph objectGraph = ObjectGraph.create(new BrewingMethodModule()); CoffeeShopApp coffeeShopApp = objectGraph.get(CoffeeShopApp.class); coffeeShopApp.run(); } @Moduleで注入先クラスを指定 @Providesを付与したメソッドで、注入するオ ブジェクトを生成する処理を記述。 Daggerライブラリのオブジェクト生成処理 (BrewingMethodModuleの設定を読み込 んで、CoffeeShopAppオブジェクトを生 成)。 @Injectが付与されている CofeeShopApp#cofeeShop, CofeeShop#resingMethod にオブジェクトが注入された状態の CoffeeShopAppオブジェクトが生成されま す。
  15. 15. Daggerが依存性が注入されたオブジェクトを生成することで、 CofeeShopAppから直接インスタンス化する記述をなくすことができ、 以下のモジュール間の依存関係を外部に逃がすことができました。  CofeeShop ー Siphon  CoffeeShopApp ー CofeeShop
  16. 16. で、DI使うと    なにがよくなるの?
  17. 17. DIを使う場合の利点 ● モジュール間の依存関係を弱めることができる(保守性が高まる) ● 単体テストが楽になる (Aモジュールが完成する前にBモジュールをテストできる、外から特定のモジュールをモック に差し替えることができる) 実際やってみて、特にテストコードを書くとき、Mockオブ ジェクトに差し替えてテストながせるのが心地よかったで す。
  18. 18. public class CoffeeShopTest { @Inject CoffeeShop coffeeShop; @Inject BrewingMethod brewingMethod; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module(includes = BrewingMethodModule.class, injects = CoffeeShopTest.class, overrides = true) static class TestModule { @Provides @Singleton public BrewingMethod provideBrewingMethod() { return Mockito.mock(BrewingMethod.class); } } @Test public void testBrewCoffee() { Mockito.when(brewingMethod.brew()).thenReturn("テストコーヒー"); String result = coffeeShop.brewCoffee(); Mockito.verify(brewingMethod, Mockito.times(1)).brew(); assertThat(result, is("テストコーヒーが出来上がりました [_]P")); } DIを利用したテストコードの一例 CoffeShopのテストコードです。 BrewingMethodクラスのオブジェクトは モックに置き換えてテストを実行してい ます。 CoffeeShopから BrewingMethod#brewメソッドが1回呼 ばれていること、 CoffeeShop#brewCoffeeメソッドの結 果が正しいこと を確認するテストコードです。
  19. 19. public class CoffeeShopTest { @Inject CoffeeShop coffeeShop; @Inject BrewingMethod brewingMethod; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module(includes = BrewingMethodModule.class, injects = CoffeeShopTest.class, overrides = true) static class TestModule { @Provides @Singleton public BrewingMethod provideBrewingMethod() { return Mockito.mock(BrewingMethod.class); } } @Test public void testBrewCoffee() { Mockito.when(brewingMethod.brew()).thenReturn("テストコーヒー"); String result = coffeeShop.brewCoffee(); Mockito.verify(brewingMethod, Mockito.times(1)).brew(); assertThat(result, is("テストコーヒーが出来上がりました [_]P")); } BrewingMethodModuleを、BrewingMethod のモックを返却するように上書きしている。
  20. 20. public class CoffeeShopTest { @Inject CoffeeShop coffeeShop; @Inject BrewingMethod brewingMethod; @Before public void setUp() { ObjectGraph.create(new TestModule()).inject(this); } @Module(includes = BrewingMethodModule.class, injects = CoffeeShopTest.class, overrides = true) static class TestModule { @Provides @Singleton public BrewingMethod provideBrewingMethod() { return Mockito.mock(BrewingMethod.class); } } @Test public void testBrewCoffee() { Mockito.when(brewingMethod.brew()).thenReturn("テストコーヒー"); String result = coffeeShop.brewCoffee(); Mockito.verify(brewingMethod, Mockito.times(1)).brew(); assertThat(result, is("テストコーヒーが出来上がりました [_]P")); } 本クラスへの依存性注入の実行 CoffeeShop#brewingMethod、 brewingMethodへ BrewingMethodのMockを注入する。 (TestModule#provideBrewingMethodに Singletonアノテーションを付与しているので すべて同じオブジェクトが注入される)。 注入した、モックオブジェクトの振る舞 いを設定して、テストを実行。 ※BrewingMethod#brew()が呼ばれ た際に”テストコーヒー”が返却されるよ う設定。
  21. 21. DIコンテナって 他にはどんなのがあるの?
  22. 22. 代表的なDIコンテナ ● Spring Framework ● JavaEE (CDI) ● Seasar2 ● Zend Framework 2 ● Dagger ● Dagger2 ● Proton ● RoboGuice
  23. 23. なんかJavaばっかりじゃね?
  24. 24. 言語とDIコンテナ ● Javaなどの静的言語では色々と種類があるが、 RubyやPythonなどの動的言語ではあまり使われていない(と思う)。 私は一時DIについて関心を持って、いろいろ調べてみたし、 自分でDIコンテナ を実装してみたりもした。 でも、RubyでならDIコンテナがわずか20行で記述で きる上、 よく考えてみたら、その20行も、なくてもほぼ同じことが簡単に実現で きることに気がついた時、 DIってのは硬直した言語のための技術なんだと気 がついた。 Matzにっき より http://www.rubyist.net/~matz/20091003.html
  25. 25. どんな所でDIを使うべき?
  26. 26. DIの使いどころ ● (当たり前ですが) DIを前提にしたフレームワークを使用するとき。 ● Javaを使用しており、テスト駆動開発(TDD)とかテストコードを書くことが前提のプロジェクト。 ● 実装があるモジュールに依存してるが、そのモジュールは未完成、でも単体テストを始めない といけないんだよ〜みたいなことになりそうなとき。 採用したときのコスト(学習コストなど)に比べて、モ ジュール間の依存関係を弱めることが重要な時に使用 するべき。 何でもかんでもDIすればいいってもんでもない。
  27. 27. ご清聴ありがとうございました

×