Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency Injection Made Simple (360|AnDev)

Dependency Injection Made Simple (360|AnDev)

Version of talk given at 360|AnDev.

Daniel Lew

July 29, 2016
Tweet

More Decks by Daniel Lew

Other Decks in Programming

Transcript

  1. • Data provider depends on database • Image Loader depends

    on HTTP • REST depends on HTTP • REST depends on deserializer • Login screen depends on all the above
  2. "Dependency Injection" is a 25-dollar term for a 5-cent concept.

    http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html ~James Shore
  3. public class Example {
 private final Dependency dependency;
 
 public

    Example() {
 dependency = new Dependency();
 }
 } VS public class Example {
 private final Dependency dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 }
  4. public class Example {
 private final Dependency dependency;
 
 public

    Example() {
 dependency = new Dependency();
 }
 } VS public class Example {
 private final Dependency dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 }
  5. • Constructor injection public class Example {
 private final Dependency

    dependency;
 
 public Example(Dependency dependency) {
 this.dependency = dependency;
 }
 } • Method injection public class Example {
 private Dependency dependency;
 
 public Example() { }
 
 public void setDependency(Dependency dependency) {
 this.dependency = dependency;
 }
 }
  6. Dependency inversion principle • High-level modules should not depend on

    low-level modules. Both should depend on abstractions. • Abstractions should not depend on details. Details should depend on abstractions.
  7. What if… • …You want to read from disk? enum

    InputDevice {
 KEYBOARD, DISK
 }
 
 void copy(InputDevice input) {
 char c;
 while (true) {
 if (input == InputDevice.KEYBOARD) {
 c = readKeyboard();
 } else if (input == InputDevice.DISK) {
 c = readDisk();
 } else {
 throw new IllegalArgumentException("Whoops!");
 }
 
 if (c == -1) {
 return;
 }
 
 writePrinter(c);
 }
 }
  8. What if… • …You want to write to the monitor?

    enum OutputDevice {
 PRINTER, MONITOR
 }
 
 void copy(OutputDevice outputDevice) {
 char c;
 while ((c = readKeyboard()) != -1) {
 if (outputDevice == OutputDevice.PRINTER) {
 writePrinter(c);
 } else if (outputDevice == OutputDevice.MONITOR) {
 writeMonitor(c);
 } else {
 throw new IllegalArgumentException("Whoops again!");
 }
 }
 }
  9. • What do we test in the read / write

    modules? • How do we test copy?
  10. interface Reader {
 char read();
 }
 
 interface Writer {


    void write(char c);
 }
 
 void copy(Reader reader, Writer writer) {
 char c;
 while ((c = reader.read()) != -1) {
 writer.write(c);
 }
 }
  11. @Test
 public void testCopy() {
 TestReader reader = new TestReader("Hello,

    world!");
 TestWriter writer = new TestWriter();
 copy(reader, writer);
 assertEquals("Hello, world!", writer.getWritten());
 }
  12. But Why? • Share dependencies • Configure dependencies externally •

    Clean separation of modules • Easy testing • Easy debugging
  13. Framework Advantages • Handles injection busywork • Handles logic (e.g.

    singletons, lazy loading) • Unified system for dependencies
  14. Frameworks • Dagger 1 • Dagger 2 • Guice •

    Spring • PicoContainer • …And on and on and on…
  15. // Greeter.java public class Greeter {
 @Inject public Greeter() {

    }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();
  16. // Greeter.java public class Greeter {
 @Inject public Greeter() {

    }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();
  17. // Greeter.java public class Greeter {
 @Inject public Greeter() {

    }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();
  18. // Greeter.java public class Greeter {
 @Inject public Greeter() {

    }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();
  19. // Greeter.java public class Greeter {
 @Inject public Greeter() {

    }
 
 public void helloWorld() {
 System.out.println("Hello, world!");
 }
 } // MyModule.java @Module(injects = Greeter.class)
 public final class MyModule { } // Retrieving Greeter ObjectGraph objectGraph = ObjectGraph.create(new MyModule()); Greeter greeter = objectGraph.get(Greeter.class); greeter.helloWorld();
  20. // Text.java
 public class Text {
 @Inject public Text() {

    }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }
  21. // Text.java
 public class Text {
 @Inject public Text() {

    }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }
  22. // Text.java
 public class Text {
 @Inject public Text() {

    }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }
  23. // Text.java
 public class Text {
 @Inject public Text() {

    }
 
 public String getText() {
 return "Hello, world!";
 }
 }
 // Greeter.java
 public class Greeter {
 private final Text text;
 
 @Inject public Greeter(Text text) {
 this.text = text;
 }
 
 public void sayText() {
 System.out.println(text.getText());
 }
 }
  24. // Greeter.java
 public class Greeter {
 @Inject Text text;
 


    public void sayText() {
 System.out.println(text.getText());
 }
 }
  25. // Greeter.java
 public class Greeter {
 @Inject Text text;
 


    public void sayText() {
 System.out.println(text.getText());
 }
 }
  26. public class ManyDependencies {
 @Inject Dep1 eevee;
 @Inject Dep2 ditto;


    @Inject Dep3 snorlax;
 @Inject Dep4 articuno;
 @Inject Dep5 dragonite;
 @Inject Dep6 mew;
 }
  27. Providers • Explicitly provide dependencies • Greater control over dependency

    • Can’t annotate dependency • Dependency inversion
  28. Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides

    Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }
  29. Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides

    Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }
  30. Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides

    Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }
  31. Providers @Module(injects = Greeter.class)
 public final class MyModule {
 @Provides

    Text provideText() {
 return new Text("Hello, World!");
 }
 
 @Provides Greeter provideGreeter(Text text) {
 return new Greeter(text);
 }
 }
  32. Providing Interfaces public interface Reader {
 char read();
 }
 public

    class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }
  33. Providing Interfaces public interface Reader {
 char read();
 }
 public

    class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }
  34. Providing Interfaces public interface Reader {
 char read();
 }
 public

    class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }
  35. Providing Interfaces public interface Reader {
 char read();
 }
 public

    class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }
  36. Providing Interfaces public interface Reader {
 char read();
 }
 public

    class KeyboardReader implements Reader {
 @Override
 public char read() { /* implementation */ }
 }
 @Module(injects = Reader.class)
 public final class MyModule {
 @Provides Reader provideReader() {
 return new KeyboardReader();
 }
 }
  37. Multiple Modules • Compile time @Module(includes = HttpModule.class)
 public final

    class RestModule { } • Runtime ObjectGraph.create(new RestModule(), new HttpModule()); Warning!
  38. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  39. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  40. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  41. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  42. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  43. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  44. // HttpModule.java @Module(library = true)
 public final class HttpModule {


    @Provides OkHttp provideOkHttp() {
 return new OkHttp();
 }
 }
 // RestModule.java @Module(complete = false)
 public final class RestModule {
 @Provides Retrofit provideRetrofit(OkHttp okHttp) {
 return new Retrofit(okHttp);
 }
 }
  45. Other Dagger Features • Singletons @Provides @Singleton Greeter provideGreeter() •

    Lazy injecting @Inject Lazy<Text> text; • Qualifiers @Provides @Named("Greeter1") Greeter provideGreeter()
  46. Dagger + Gradle apply plugin: 'com.neenbedankt.android-apt'
 
 dependencies {
 apt

    'com.squareup.dagger:dagger-compiler:1.2.2'
 compile 'com.squareup.dagger:dagger:1.2.2'
 } https://bitbucket.org/hvisser/android-apt
  47. Injecting Android • Activity, Service, Fragment, etc… • Problem: We

    don’t control construction! • Solution: ObjectGraph.inject()
  48. Injecting Activities public class MyActivity extends Activity {
 
 @Inject

    Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }
  49. Injecting Activities public class MyActivity extends Activity {
 
 @Inject

    Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }
  50. Injecting Activities public class MyActivity extends Activity {
 
 @Inject

    Dependency dependency;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 ObjectGraph objectGraph = ((MyApplication) getApplication()).getObjectGraph();
 objectGraph.inject(this);
 
 setContentView(R.layout.main);
 }
 }