$30 off During Our Annual Pro Sale. View Details »

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. Dependency injection made simple
    Dan Lew

    View Slide

  2. What is a
    dependency?

    View Slide

  3. A depends on B

    View Slide

  4. • 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

    View Slide

  5. What is dependency
    injection?

    View Slide

  6. "Dependency Injection" is
    a 25-dollar term for a
    5-cent concept.
    http://www.jamesshore.com/Blog/Dependency-Injection-Demystified.html
    ~James Shore

    View Slide

  7. 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;

    }

    }

    View Slide

  8. 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;

    }

    }

    View Slide

  9. • 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;

    }

    }

    View Slide

  10. View(Context context)

    View Slide

  11. View Slide

  12. But Why?
    • Share dependencies
    • Configure dependencies externally

    View Slide

  13. Context wrappedContext =
    new ContextThemeWrapper(context, R.style.MyTheme);

    View view = new View(wrappedContext);

    View Slide

  14. 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.

    View Slide

  15. A depends on B

    View Slide

  16. A is coupled to B

    View Slide

  17. View Slide

  18. View Slide

  19. void copy() {

    char c;

    while ((c = readKeyboard()) != -1) {

    writePrinter(c);

    }

    }

    View Slide

  20. 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);

    }

    }

    View Slide

  21. 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!");

    }

    }

    }

    View Slide

  22. • What do we test in the read / write modules?
    • How the hell do we test copy?!

    View Slide

  23. What if…
    • You want to change readKeyboard() or
    writePrinter()?
    void copy() {

    char c;

    while ((c = readKeyboard()) != -1) {

    writePrinter(c);

    }

    }

    View Slide

  24. View Slide

  25. 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);

    }

    }

    View Slide

  26. But Why?
    • Share dependencies
    • Configure dependencies externally
    • Clean separation of modules

    View Slide

  27. View Slide

  28. But Why?
    • Share dependencies
    • Configure dependencies externally
    • Clean separation of modules
    • Easy testing

    View Slide

  29. View Slide

  30. @Test

    public void testCopy() {

    TestReader reader = new TestReader("Hello, world!");

    TestWriter writer = new TestWriter();

    copy(reader, writer);

    assertEquals("Hello, world!", writer.getWritten());

    }

    View Slide

  31. But Why?
    • Share dependencies
    • Configure dependencies externally
    • Clean separation of modules
    • Easy testing
    • Easy debugging

    View Slide

  32. …Why not?
    • More verbose
    • More complex

    View Slide

  33. View Slide

  34. Framework Advantages
    • Handles injection busywork
    • Handles logic (e.g. singletons, lazy loading)
    • Unified system for dependencies

    View Slide

  35. Frameworks
    • Dagger 1
    • Dagger 2
    • Guice
    • Spring
    • PicoContainer
    • …And on and on and on…

    View Slide

  36. Dagger
    Directed Acyclic Graph

    View Slide

  37. Object Graph

    View Slide

  38. Greeter

    View Slide

  39. // 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();

    View Slide

  40. // 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();

    View Slide

  41. // 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();

    View Slide

  42. // 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();

    View Slide

  43. // 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();

    View Slide

  44. Greeter
    Text

    View Slide

  45. // 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());

    }

    }

    View Slide

  46. // 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());

    }

    }

    View Slide

  47. // 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());

    }

    }

    View Slide

  48. // 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());

    }

    }

    View Slide

  49. @Inject

    public ManyDependencies(

    Dep1 never,

    Dep2 going,

    Dep3 to,

    Dep4 give,

    Dep5 you,
    Dep6 up

    )

    View Slide

  50. // Greeter.java

    public class Greeter {

    @Inject Text text;


    public void sayText() {

    System.out.println(text.getText());

    }

    }

    View Slide

  51. // Greeter.java

    public class Greeter {

    @Inject Text text;


    public void sayText() {

    System.out.println(text.getText());

    }

    }

    View Slide

  52. public class ManyDependencies {

    @Inject Dep1 never;

    @Inject Dep2 going;

    @Inject Dep3 to;

    @Inject Dep4 let;

    @Inject Dep5 you;

    @Inject Dep6 down;

    }

    View Slide

  53. Providers
    • Explicitly provide dependencies
    • Greater control over dependency
    • Can’t annotate dependency
    • Dependency inversion

    View Slide

  54. 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);

    }

    }

    View Slide

  55. 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);

    }

    }

    View Slide

  56. 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);

    }

    }

    View Slide

  57. 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);

    }

    }

    View Slide

  58. Providing Interfaces

    View Slide

  59. 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();

    }

    }

    View Slide

  60. 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();

    }

    }

    View Slide

  61. 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();

    }

    }

    View Slide

  62. 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();

    }

    }

    View Slide

  63. 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();

    }

    }

    View Slide

  64. Single Module

    View Slide

  65. Multiple Modules

    View Slide

  66. Multiple Modules
    • Compile time
    @Module(includes = HttpModule.class)

    public final class RestModule { }
    • Runtime
    ObjectGraph.create(new RestModule(), new HttpModule());
    Warning!

    View Slide

  67. View Slide

  68. Unused!
    Incomplete!

    View Slide

  69. // 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);

    }

    }

    View Slide

  70. // 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);

    }

    }

    View Slide

  71. // 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);

    }

    }

    View Slide

  72. // 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);

    }

    }

    View Slide

  73. // 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);

    }

    }

    View Slide

  74. // 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);

    }

    }

    View Slide

  75. // 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);

    }

    }

    View Slide

  76. 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();

    }

    }

    View Slide

  77. 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();

    }

    }

    View Slide

  78. 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();

    }

    }

    View Slide

  79. 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();

    }

    }

    View Slide

  80. 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();

    }

    }

    View Slide

  81. 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();

    }

    }

    View Slide

  82. Other Dagger Features
    • Singletons
    @Provides @Singleton Greeter provideGreeter()
    • Lazy injecting
    @Inject Lazy text;
    • Qualifiers
    @Provides @Named("Greeter1") Greeter provideGreeter()

    View Slide

  83. View Slide

  84. Dagger + Gradle
    dependencies {

    provided 'com.squareup.dagger:dagger-compiler:1.2.2'

    compile 'com.squareup.dagger:dagger:1.2.2'

    }

    View Slide

  85. 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

    View Slide

  86. Dagger + ProGuard
    • Google ProGuard config
    • Use Dagger 2

    View Slide

  87. Injecting Android
    • Activity, Service, Fragment, etc…
    • Problem: We don’t control construction!
    • Solution: ObjectGraph.inject()

    View Slide

  88. 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);

    }

    }

    View Slide

  89. 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);

    }

    }

    View Slide

  90. 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);

    }

    }

    View Slide

  91. u2020
    • Unicode character for “dagger”: †
    • Injector pattern
    • https://github.com/JakeWharton/u2020

    View Slide

  92. Thanks!
    @danlew42
    danlew.net

    View Slide