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

IDCamp Sharing Session - IoC And Android Archit...

IDCamp Sharing Session - IoC And Android Architecture

Inversion of Control Principles
Android MV Whatever + Clean Architecture

live code source available at https://github.com/ekosuhariyadi/IDCamp

Avatar for Eko Suhariyadi

Eko Suhariyadi

November 09, 2019

More Decks by Eko Suhariyadi

Other Decks in Technology

Transcript

  1. Case study interface LeagueService { @GET("all_leagues.php") fun allLeagues(): Single<LeagueResponse> }

    
 class BadLeagueRepository : LeagueRepository { private val leagueService by lazy { /* construct instance of LeagueService */ } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  2. Case study class BadLeagueRepository : LeagueRepository { private val leagueService

    by lazy { /* construct instance of LeagueService */ } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  3. Case study class BadLeagueRepository : LeagueRepository { private val leagueService

    by lazy { Retrofit.Builder() .baseUrl("base.url") .client(OkHttpClient()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .build() .create(LeagueService::class.java) } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  4. Case Study • This way to initialise leagueService have some

    disadvantages • Retrofit config scattered in many places • We can’t limit instance that will be created
 eg: when we just need single instance of object that heavy to instantiate • When changes happen, we need to change in many places • We can’t change the dependencies into mock/test double in isolated (unit) test
  5. Inversion of Control class BadLeagueRepositoryTest { @Test fun get_list_of_league() {

    /** * How to inject/pass LeagueService into Repository ? */ BadLeagueRepository().leagues() // assert the result } }
  6. Inversion of Control • Two ways of injection • Setter-Injection

    • Pass dependencies by setter method • Contructor-Injection • Pass dependencies through constructor arguments
  7. IoC Setter-Injection class BadLeagueRepository : LeagueRepository { private val leagueService

    by lazy { Retrofit.Builder() .baseUrl("base.url") .client(OkHttpClient()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .build() .create(LeagueService::class.java) } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  8. IoC Setter-Injection class BadLeagueRepository : LeagueRepository { private lateinit var

    leagueService: LeagueService fun setLeagueService(leagueService: LeagueService) { this.leagueService = leagueService } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  9. IoC Setter-Injection class BadLeagueRepositoryTest { @Test fun get_list_of_league() { /**

    * How to inject/pass LeagueService into Repository ? */ BadLeagueRepository().leagues() // assert the result } }
  10. IoC Setter-Injection class BadLeagueRepositoryTest { @Test fun get_list_of_league() { val

    leagueService = Mockito.mock(LeagueService::class.java) BadLeagueRepository() .apply { setLeagueService(leagueService) } .leagues() // assert the result } }
  11. IoC Constructor-Injection class BadLeagueRepository : LeagueRepository { private val leagueService

    by lazy { Retrofit.Builder() .baseUrl("base.url") .client(OkHttpClient()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create()) .build() .create(LeagueService::class.java) } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  12. IoC Constructor-Injection class BadLeagueRepository( private val leagueService: LeagueService ) :

    LeagueRepository { override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
  13. IoC Constructor-Injection class BadLeagueRepositoryTest { @Test fun get_list_of_league() { /**

    * How to inject/pass LeagueService into Repository ? */ BadLeagueRepository().leagues() // assert the result } }
  14. IoC Constructor-Injection class BadLeagueRepositoryTest { @Test fun get_list_of_league() { val

    leagueService = Mockito.mock(LeagueService::class.java) BadLeagueRepository(leagueService) .leagues() // assert the result } }
  15. Dependency Injection • 3 components of DI • Dependency •

    Objects that we want to use • Container • Where we provide (and manage) dependencies • Injection • Passing dependencies that needed by client
  16. Dependency injection for five-year-olds When you go and get things

    out of the refrigerator for yourself, you can cause problems. 
 
 You might leave the door open, you might get something Mommy or Daddy doesn't want you to have. 
 You might even be looking for something we don't even have or which has expired. 
 What you should be doing is stating a need, 
 
 "I need something to drink with lunch," 
 and then we will make sure you have something when you sit down to eat. John Munsch, 28 October 2009
  17. Super Class • Hard to maintain • Does not have

    Separation of Concern • Hard to test
  18. Well Known Architecture • MVC • MVP • MVVM •

    MVI • MV Whatever 
 *we will just focus on MVP and MVVM
  19. MVP // Model interface Repository { fun getLeague(): Single<List<League>> }

    // View interface View { fun showLoading() fun hideLoading() fun showLeague(leagues: List<League>) } // Presenter interface Presenter { fun attachView(view: View) fun detachView(view: View) fun loadLeagues() }
  20. MVP • Model • Source of data • View •

    UI layer • Presenter • Handle presentation logic • Interact with Model and updates View
  21. MVP • Pros • Better separation of concerns, UI &

    presentational logic are decoupled • Presentation logic can be unit tested • Simple UI tests since presentation logic handled by Presenter • Just need to test rendering result and interaction with Presenter • Cons • Has dependency to View instance might causing memory leak 
 when not handle properly (attach-detach mechanism)
  22. (Plain) MVVM // Model interface Repository { fun getLeague(): Single<List<League>>

    } // View interface View { fun showLoading() fun hideLoading() fun showLeague(leagues: List<League>) } // ViewModel interface ViewModel { fun loadLeagues() val isLoading: LiveData<Boolean> val leagues: LiveData<List<League>> }
  23. (Plain) MVVM • Model • Source of data • View

    • UI layer • Observe ViewModel • ViewModel • Handle presentation logic • Interact with Model and produces changes to be observed by View
  24. (Plain) MVVM • Pros • Better separation of concerns, UI

    & presentational logic are decoupled • Presentation logic can be unit tested • Simple UI tests • Decoupled MVP: ViewModel (Presenter) no longer has dependency to View • Reactive View updates • Cons • Might have too many objects that will be observed by View
  25. MVVM + ViewState // Model interface Repository { fun getLeague():

    Single<List<League>> } // View sealed class ViewState interface View { fun render(viewState: ViewState) } // ViewModel interface ViewModel { fun loadLeagues() val viewState: LiveData<ViewState> }
  26. MVVM + ViewState • Model • Source of data •

    View • UI layer • Observe ViewModel • ViewState as representation of the state of View • ViewModel • Handle presentation logic • Interact with Model and produces changes to be observed by View
  27. MVVM + ViewState • Pros • Better separation of concerns,

    UI & presentational logic are decoupled • Presentation logic can be unit tested • Simple UI tests • Decoupled MVP: ViewModel (Presenter) no longer has dependency to View • Reactive View updates • Single source of truth for ViewState
  28. Goals of Clean Architecture • Independent of Frameworks • Testable

    • Independent of UI • Independent of Database • Independent of any external agency (such as OS, Network, etc)
 Our business rule simply don’t know anything at all about outside world.
  29. Separation of Concern by Layering • From Bottom to Up

    • Gateway -> code to interact with outside world such as OS, Database, Network • Repository -> domain logic to handle data CRUD • Use Case -> code to handle business logic • Combine with MV-Whatever