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

Dependency injection crash course

Avatar for Robin Robin
April 27, 2017

Dependency injection crash course

A lightning talk about using dependency injection in an Android app to easy code testability and maintain SRP.

Avatar for Robin

Robin

April 27, 2017
Tweet

More Decks by Robin

Other Decks in Programming

Transcript

  1. The need for DI Testable? - Mocking static is not

    simple - What if behaviour related to Android SDK? - What if final/private fields? SRP/Re-testing behaviours public class BookInteractor {
 public void get(int bookId, Callback callback) {
 int collectionId = Collection.getCurrent().getId();
 String serverUrl = NetworkService.getBaseUrl();
 String url = serverUrl + collectionId + "/" + bookId + "?auth_token="
 + User.getCurrent().getAuthToken();
 
 NetworkService.get(url).setCallback(callback).go();
 }
 }
  2. We need the right stuff at the right time All

    is provided, all is mockable public class BookInteractor {
 private final NetworkService mNetworkService;
 private final Collection mCollection;
 
 public BookInteractor(NetworkService networkService, Collection collection) {
 mNetworkService = networkService;
 mCollection = collection;
 }
 
 public void get(int bookId, Callback callback) {
 int collectionId = mCollection.getId(); String baseUrl = mNetworkService.getServerUrl();
 String url = baseUrl + "/" + collectionId + "/" + bookId + “?auth_token=" + User.getCurrent().getAuthToken();
 
 mNetworkService.get(url).setCallback(callback).go();
 }
 } networkService & collection are "dependencies". They are "injected".
  3. Down the rabbit hole public class MainActivity extends AppCompatActivity implements

    BookView {
 
 private BookPresenter mBookPresenter;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 mBookPresenter = new BookPresenter(this, new BookInteractor(networkService??, collection??));
 }
 } Who is responsible for providing the dependencies? - adb, live-coding of 04/19
  4. Using a DI framework public class App extends Application {


    private ObjectGraph mObjectGraph;
 
 @Override
 public void onCreate() {
 super.onCreate();
 mObjectGraph = new ObjectGraph(this);
 } public ObjectGraph getObjectGraph() {
 return mObjectGraph;
 }
 } Step 1: Make the object graph accessible at runtime
  5. Using a DI framework public class BookInteractorImpl implements BookInteractor {


    private final NetworkService mNetworkService;
 private final Collection mCollection;
 
 @Inject BookInteractorImpl(NetworkService networkService, Collection collection) {
 mNetworkService = networkService;
 mCollection = collection;
 }
 
 ...
 } Step 2: Annotate the required constructors
  6. Using a DI framework Step 3: Create a business module

    @Module(include = {NetworkModule.class})
 public class BookModule {
 @Provides
 Collection provideCollection() {
 ...
 return collection;
 } @Provides
 BookInteractor provideBookInteractor(BookInteractorImpl bookInteractor) {
 ...
 return bookInteractor;
 }
 } Object construction logic is isolated.
  7. Using a DI framework public class MainActivity extends AppCompatActivity {


    
 @Inject BookInteractor mBookInteractor;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 getGraphObject().plus(new BookModule()).inject(this);
 }
 } Annotated fields are injected Step 4: INJECT getInjector gets the object graph from app level.
  8. Using a DI framework “But wait, how to provide mocks,

    then?” Dagger2 + DaggerMock ❤ TL;DR DaggerMock auto-replaces modules with mocks.
  9. Object construction logic is isolated Modules are reusable Dependencies are

    mockable ✅ We happy? ⚠ Tedious without a framework Overkill for small projects TON of added complexity Generated code / Method count