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

Ingredients for a healthy codebase

Romain Piel
November 10, 2015

Ingredients for a healthy codebase

Your app lives around monolithic Activities/Fragments? You think that singletons are amazing or you can’t get away from them? You find your app hard to test?

Writing robust and maintainable software is complex and we know that designing the general architecture of an app is complicated. The goal of this talk is to give you ingredients to build a healthy codebase for your Android app. By going through the entire stack from the data layer to the presentation layer, we will see how to make it maintainable and testable.

Romain Piel

November 10, 2015
Tweet

More Decks by Romain Piel

Other Decks in Technology

Transcript

  1. Unit tests App Mary John Francis Hard to catch up

    with the coverage & too many Android dependencies
  2. App

  3. Systems should be • Independent of Frameworks • Testable •

    Independent of UI • Independent of Database • Independent of any external agency Clean architecture
  4. Domain layer • Orchestrates the flow of data with “use

    cases” • Offers its services to the presentation layer
  5. Domain layer • Orchestrates the flow of data with “use

    cases” • Offers its services to the presentation layer • Pure Java module • No Android UI dependencies • No dependency to external source (db, content provider, shared preferences…)
  6. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle • Receives

    user inputs • Associated to specific view layouts (ListActivity)
  7. <activity android:name=".SearchActivity"/> • Tied to the view lifecycle • Receives

    user inputs • Associated to specific view layouts (ListActivity) Your very first Android Activity will be likely to be huge and untestable
  8. View Presenter Model Presentation layer interface SearchPresenter {
 void searchArtist(String

    searchTerm);
 void clickArtist(Artist artist);
 } interface SearchView {
 void showProgress();
 void hideProgress();
 void showArtists(List<Artist> artists);
 }
  9. Model vs. View model class Artist {
 String displayName;
 String

    uri;
 String id;
 LocalDate onTourUntil;
 }
  10. class ArtistViewModel {
 String name; boolean isOnTour; } public class

    ArtistViewHolder extends ViewHolder {
 
 @Bind(R.id.artist_name)
 TextView artistName; @Bind(R.id.on_tour)
 TextView onTour;
 
 public ArtistViewHolder(View itemView) {
 super(itemView);
 ButterKnife.bind(this, itemView);
 }
 
 @Override
 public void bind(ArtistViewModel viewModel) {
 artistName.setText(artistViewModel.name);
 onTour.setVisibility(artistViewModel.isOnTour ? View.VISIBLE : View.GONE );
 }
 }
  11. Presentation layer interface SearchPresenter {
 void searchArtist(String searchTerm);
 void clickArtist(ArtistViewModel

    artist);
 } interface SearchView {
 void showProgress();
 void hideProgress();
 void showArtists(List<ArtistViewModel> artists);
 } View Presenter View model
  12. Presentation layer class SearchFragment extends Fragment implements SearchView {
 


    SearchPresenter searchPresenter;
 
 @Override
 void showProgress() {
 // ...
 }
 
 @Override
 void hideProgress() {
 // ...
 }
 
 @Override
 void showArtists(List<ArtistViewModel> artists) {
 // ...
 } {...}
 }
  13. Presentation layer class SearchFragment extends Fragment implements SearchView {
 


    SearchPresenter searchPresenter;
 {...} 
 void onItemClick(ArtistViewModel artist) {
 searchPresenter.clickArtist(artist); }
 
 void search(String searchTerm) {
 searchPresenter.searchArtist(searchTerm); }
 }
  14. interface ArtistRepository {
 Observable<List<Artist>> search(String searchTerm);
 } Domain layer Data

    layer interface SearchArtistUseCase<VM extends ViewModel> {
 void subscribe(Subscriber<VM> subscriber); void unsubscribe();
 } Communication Rx all the things!
  15. interface ArtistRepository {
 Observable<List<Artist>> search(String searchTerm);
 } Presentation layer Domain

    layer Data layer interface SearchArtistUseCase<VM extends ViewModel> {
 void subscribe(Subscriber<VM> subscriber); void unsubscribe();
 } interface SearchPresenter {
 void searchArtist(String searchTerm); void onDestroy();
 } Communication Rx all the things!
  16. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation
  17. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation • Defines the vocabulary of the collaboration
  18. Structure “Program to an interface, not an implementation” — Erich

    Gamma • Decouple the client from the implementation • Defines the vocabulary of the collaboration • Easy to test
  19. Testing Presentation layer Domain layer Data layer Unit SearchUseCase ArtistRepository

    SearchPresenter SearchFragment Testable without Robolectric
  20. Testing Presentation layer Domain layer Data layer Unit SearchUseCase ArtistRepository

    SearchPresenter SearchFragment Testable without Robolectric Testable with Robolectric
  21. Testing Presentation layer Domain layer Data layer UI .json class

    ArtistRepositoryTestImpl {
 
 JsonLoader jsonLoader;
 
 Observable<List<Artist>> search(String searchTerm) {
 return jsonLoader.load(“search/artist.json”);
 }
 }
  22. Conclusion • Choose an architecture and stick with it •

    Test while you code • Always do what’s best for the codebase, not for the feature you’re implementing
  23. Links • Uncle Bob’s clean architecture 
 http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
 • Fernando

    Cejas - Architecting Android…The clean way?
 http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way
 https://github.com/android10/Android-CleanArchitecture
 • Martin Fowler - The repository pattern
 http://martinfowler.com/eaaCatalog/repository.html
 • Erich Gamma - Design Principles from Design Patterns
 http://www.artima.com/lejava/articles/designprinciples.html