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 */ } }
by lazy { /* construct instance of LeagueService */ } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
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 */ } }
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
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 */ } }
leagueService: LeagueService fun setLeagueService(leagueService: LeagueService) { this.leagueService = leagueService } override fun leagues(): Single<List<League>> { /* use leagueService to retrieve list of league */ } }
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 */ } }
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
// 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() }
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)
} // 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>> }
& 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
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
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
• 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.
• 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