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

Intro To Dependency Injection

Or Bar
September 26, 2016

Intro To Dependency Injection

Dependency Injection continues to grow in popularity due to it's code simplification effects. If you've haven't had a chance to learn and apply DI yet, this introduction is for you.

This talk will cover the theory of "Inversion of Control", introduce Dependency injection, how it can be used within Android to simplify your App's design and help you write more testable code

Or Bar

September 26, 2016
Tweet

More Decks by Or Bar

Other Decks in Technology

Transcript

  1. - What is a Dependency? - Dependency Injection - Dependency

    Inversion Principle - Dependency Injection Frameworks Agenda
  2. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 
 public void doStuff() {
 ...
 mDatabaseHelper.loadUsers();
 ...
 }
 }
  3. - Dependency injection is a software design pattern that implements

    inversion of control for resolving dependencies - An injection is the passing of a dependency to a dependent object to use
  4. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 } public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }
  5. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    mDatabaseHelper = new DatabaseHelper();
 }
 } public class Example {
 DatabaseHelper mDatabaseHelper; public Example() {
 mDatabaseHelper = new DatabaseHelper();
 }
 }
 public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 } public class Example {
 DatabaseHelper mDatabaseHelper; 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }

  6. public class Example {
 DatabaseHelper mDatabaseHelper;
 
 public Example() {


    }
 
 public void setDatabaseHelper(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 }
 }
  7. public class ExampleTest {
 @Mock DatabaseHelper mockDatabaseHelper;
 
 @Test
 public

    void testExample_doStuff() {
 Example example = new Example(mockDatabaseHelper);
 example.doStuff();
 mockDatabaseHelper.AssertGetDataWasCalled();
 }
 } public class Example { 
 DatabaseHelper mDatabaseHelper;
 
 public Example(DatabaseHelper databaseHelper) {
 mDatabaseHelper = databaseHelper;
 } public void doStuff() {
 ...
 mDatabaseHelper.loadUsers();
 ...
 }
 }
  8. - 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. Dependency Inversion Principle
  9. - Bare bones ebook reader - Supports PDF files -

    Prints book contents to screen Ebook Reader
  10. public class CoolEBookReader {
 public void loadPage(String bookUri, int PageNumber)

    {
 String pdfContent = getPdfContent(bookUri, PageNumber);
 displayPage(pdfContent);
 } }
  11. public class CoolEBookReader {
 enum BookType {
 PDF,
 EPUB
 }


    
 public void loadPage(BookType bookType, String bookUri, int PageNumber) {
 String pageContent;
 if (bookType == BookType.PDF) {
 pageContent = getPdfContent(bookUri, PageNumber);
 } else if (bookType == BookType.EPUB) {
 pageContent = getEpubContent(bookUri, PageNumber);
 } else {
 throw new IllegalArgumentException("Unknown book type");
 }
 displayPage(pageContent); 
 }
 }
  12. public class CoolEBookReader {
 enum BookType {
 PDF,
 EPUB
 


    }
 
 enum PrinterType {
 SCREEN,
 VOICE
 
 }
 
 public void loadPage(BookType bookType, PrinterType printerType, String bookUri, int PageNumber) {
 String pageContent;
 if (bookType == BookType.PDF) {
 pageContent = getPdfContent(bookUri, PageNumber);
 } else if (bookType == BookType.EPUB) {
 pageContent = getEpubContent(bookUri, PageNumber);
 } else {
 throw new IllegalArgumentException("Unknown book type");
 }
 
 if (printerType == PrinterType.SCREEN) {
 displayPage(pageContent);
 } else if (printerType == PrinterType.VOICE) {
 readAloudPage(pageContent);
 } else {
 throw new IllegalArgumentException("Unknown printer type");
 }
 }
 }
  13. - 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. Dependency Inversion
  14. interface Reader {
 String read(String bookUri, int pagNumber); }
 


    interface Printer {
 void print(String pageContent);
 } public class CoolEBookReader {
 public void loadPage(Reader reader, Printer printer, String bookUri, int pageNumber) {
 String pageContent = reader.read(bookUri, pageNumber);
 printer.print(pageContent);
 }
 }
  15. interface Reader {
 String read(String bookUri, int pagNumber); }
 


    interface Printer {
 void print(String pageContent);
 } public class CoolEBookReader {
 public void loadPage(Reader reader, Printer printer, String bookUri, int pageNumber) {
 String pageContent = reader.read(bookUri, pageNumber);
 printer.print(pageContent);
 }
 } public class CoolEBookReader {
 public void loadPage(String bookUri, int PageNumber) {
 String pdfContent = getPdfContent(bookUri, PageNumber);
 displayPage(pdfContent);
 } }
  16. public class CoolEBookReaderTest {
 @Mock
 Reader mockReader;
 
 @Mock
 CoolEBookReader.Printer

    mockPrinter;
 
 @Test 
 public void TestExample_loadPage() {
 CoolEBookReader coolEBookReader = new CoolEBookReader();
 coolEBookReader.loadPage(mockReader, mockPrinter, "", 1);
 // Assert Reader.read() is called
 // Assert Printer.print() is called
 }
 }
  17. - Handles object creation - Reduces boilerplate code - A

    central location for organizing dependencies - Implement common patterns natively (singleton, lazy loading, etc) Why use a framework?
  18. - Dagger 1 - Dagger 2 - Guice and RoboGuice

    - PicoContainer - Spring - Many, many, many, many more options Common Frameworks
  19. History lesson of DI on Android - RoboGuice - 2009

    - Spring for Android - 2012 - Dagger 1 - 2013 - Dagger 2 - 2015 - Tiger - 2016
  20. public class ConnectionHelper {
 private Context mContext;
 
 public ConnectionHelper(Context

    context) {
 mContext = context;
 }
 
 public boolean isConnected() {
 ConnectivityManager connectivityManager =
 (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
 return connectivityManager.getActiveNetworkInfo().isConnected();
 }
 } ConnectionHelper connectionHelper = new ConnectionHelper(context);
 
 boolean isConnected = connectionHelper.isConnected();
  21. public class ConnectionHelper {
 private Context mContext;
 
 @Inject
 public

    ConnectionHelper(Context context) {
 mContext = context;
 }
 
 public boolean isConnected() {
 ConnectivityManager connectivityManager =
 (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
 
 return connectivityManager.getActiveNetworkInfo().isConnected();
 }
 } @Inject
  22. @Provides @Module
 public class AppModule {
 private final Context mContext;


    
 public AppModule(Context context) {
 mContext = context;
 } @Provides
 public Context providesContext() {
 return mContext;
 } @Provides
 public ConnectionHelper providesConnectionHelper(Context context) {
 return new ConnectionHelper(context);
 }
 }
  23. public class App extends Application {
 private AppComponent component;
 @Override


    public void onCreate() {
 super.onCreate();
 
 AppComponent component = DaggerAppComponent.builder()
 .appModule(new AppModule(this))
 .build();
 }
 
 public AppComponent getComponent() {
 return component;
 }
 } public class App extends Application {
 private AppComponent component;
 @Override
 public void onCreate() {
 super.onCreate();
 
 AppComponent component = DaggerAppComponent.builder()
 .appModule(new AppModule(this))
 .build();
 }
 
 public AppComponent getComponent() {
 return component;
 }
 }
  24. public class MainActivity extends AppCompatActivity {
 
 @Inject
 ConnectionHelper mConnectionHelper;


    
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ((App) getApplication()).getComponent().inject(this);
 
 boolean isConnected = mConnectionHelper.isConnected();
 
 textView.setText(isConnected ? "Connected" : "Not Connected");
 }
 } public class MainActivity extends AppCompatActivity {
 
 @Inject
 ConnectionHelper mConnectionHelper;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 ((App) getApplication()).getComponent().inject(this);
 
 boolean isConnected = mConnectionHelper.isConnected();
 
 textView.setText(isConnected ? "Connected" : "Not Connected");
 }
 } @Inject - again
  25. @Provides
 @Singleton
 public Context providesContext() {
 return context;
 } @Singleton

    @Singleton
 @Component(modules = {AppModule.class})
 public interface AppComponent {
 
 void inject(MainActivity activity); }
  26. @Provides
 @Reusable
 public ConnectionHelper providesConnectionHelper(Context context) {
 return new ConnectionHelper(context);


    } @Reusable @Singleton @Reusable
 @Component(modules = {AppModule.class})
 public interface AppComponent {}
  27. @Inject
 Lazy<ConnectionHelper> mConnectionHelper;
 ... mConnectionHelper.get().isConnected(); Lazy and Provider Injections @Inject


    Provider<ConnectionHelper> mConnectionHelper;
 ... mConnectionHelper.get().isConnected();
  28. @Provides @Singleton @Named(“Adapter 1”)
 public RestAdapter providesRestAdapter1() { return …;

    } @Qualifier @Provides @Singleton @Named(“Adapter 2”) public RestAdapter providesRestAdapter2() { return …; }
  29. Subcomponents and Scopes - Subcomponents allow you to combine different

    modules at runtime - Scopes let you define the lifetime of a dependency
  30. Testing and dagger - Constructors used for injections can be

    used for testing - Test Modules for espresso