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. 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 do we test copy?

    View Slide

  23. Dependency inversion
    to the rescue

    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

    View Slide

  27. View Slide

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

    View Slide

  29. View Slide

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

    View Slide

  31. View Slide

  32. @Test

    public void testCopy() {

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

    TestWriter writer = new TestWriter();

    copy(reader, writer);

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

    }

    View Slide

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

    View Slide

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

    View Slide

  35. View Slide

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

    View Slide

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

    View Slide

  38. Dagger
    Directed Acyclic Graph

    View Slide

  39. Object Graph

    View Slide

  40. Greeter

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

  45. // 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

  46. Greeter
    Text

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

  50. // 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

  51. @Inject

    public ManyDependencies(

    Dep1 bulbasaur,

    Dep2 charmander,

    Dep3 squirtle,

    Dep4 caterpie,

    Dep5 pikachu,
    Dep6 slowpoke

    )

    View Slide

  52. // Greeter.java

    public class Greeter {

    @Inject Text text;


    public void sayText() {

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

    }

    }

    View Slide

  53. // Greeter.java

    public class Greeter {

    @Inject Text text;


    public void sayText() {

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

    }

    }

    View Slide

  54. public class ManyDependencies {

    @Inject Dep1 eevee;

    @Inject Dep2 ditto;

    @Inject Dep3 snorlax;

    @Inject Dep4 articuno;

    @Inject Dep5 dragonite;

    @Inject Dep6 mew;

    }

    View Slide

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

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

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

  60. Providing Interfaces

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

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

  66. Single Module

    View Slide

  67. Multiple Modules

    View Slide

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

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

    View Slide

  69. View Slide

  70. Unused!
    Incomplete!

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

  77. // 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

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

    View Slide

  79. View Slide

  80. Dagger + Gradle
    dependencies {

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

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

    }

    View Slide

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

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

    View Slide

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

    View Slide

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

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

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

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

    View Slide

  88. Thanks!
    @danlew42
    danlew.net

    View Slide