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

Dependency Injection Made Simple

Daniel Lew
February 06, 2016

Dependency Injection Made Simple

Presented at DevFest MN 2016.

I'm not entirely sure these slides will make sense without me actually presenting...

Daniel Lew

February 06, 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 the hell do we test copy?!
  10. What if… • You want to change readKeyboard() or writePrinter()?

    void copy() {
 char c;
 while ((c = readKeyboard()) != -1) {
 writePrinter(c);
 }
 }
  11. 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);
 }
 }
  12. @Test
 public void testCopy() {
 TestReader reader = new TestReader("Hello,

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

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

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

    Spring • PicoContainer • …And on and on and on…
  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. // 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();
  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. // 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());
 }
 }
  25. // Greeter.java
 public class Greeter {
 @Inject Text text;
 


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


    public void sayText() {
 System.out.println(text.getText());
 }
 }
  27. public class ManyDependencies {
 @Inject Dep1 never;
 @Inject Dep2 going;


    @Inject Dep3 to;
 @Inject Dep4 let;
 @Inject Dep5 you;
 @Inject Dep6 down;
 }
  28. Providers • Explicitly provide dependencies • Greater control over dependency

    • Can’t annotate dependency • Dependency inversion
  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. 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);
 }
 }
  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. 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();
 }
 }
  38. Multiple Modules • Compile time @Module(includes = HttpModule.class)
 public final

    class RestModule { } • Runtime ObjectGraph.create(new RestModule(), new HttpModule()); Warning!
  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. // 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);
 }
 }
  46. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  47. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  48. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  49. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  50. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  51. Module Overrides @Module()
 public final class MyModule {
 @Provides Writer

    provideWriter() {
 return new PrinterWriter();
 }
 }
 @Module(
 includes = MyModule.class,
 overrides = true
 )
 public final class TestModule {
 @Provides Writer provideWriter() {
 return new TestWriter();
 }
 }
  52. Other Dagger Features • Singletons @Provides @Singleton Greeter provideGreeter() •

    Lazy injecting @Inject Lazy<Text> text; • Qualifiers @Provides @Named("Greeter1") Greeter provideGreeter()
  53. 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
  54. Injecting Android • Activity, Service, Fragment, etc… • Problem: We

    don’t control construction! • Solution: ObjectGraph.inject()
  55. 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);
 }
 }
  56. 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);
 }
 }
  57. 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);
 }
 }