Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Home Improvement: Architecture & Kotlin

161 views

Published on

Two of the most relevant news from the recent Google IO were the Guide to App Architecture and the adoption of Kotlin as a first-class language. Both have a very positive impact in our applications.

In my talk I introduced and advanced architecture inspired in the Clean Architecture of Uncle Bob and I showed the impact of these two elements.

First I mentioned the components provided for integration with the lifecycle and how that saves a lot of effort to preserve view models or presenters. I briefly covered the methods that we had available until know focusing on the use of a fragment with no view that had the retained instance property set to true.

Then I covered some real scenarios explaining the improvements that Kotlin provide us with. Some examples:
- Conciseness of data classes (and limitations)
- Property observation
- Use of extensions in presentation logic
- Sealed classes for results (as an either-like type)

This is a "Code or it didn't happen" (TM) talk. ;-)

Published in: Software
  • Be the first to comment

  • Be the first to like this

Home Improvement: Architecture & Kotlin

  1. 1. Home Improvement: Architecture & Kotlin Jorge D. Ortiz-Fuentes @jdortiz
  2. 2. #AdvArchMobile A Canonical Examples Production
  3. 3. #AdvArchMobile Disclaimer
  4. 4. #AdvArchMobile Agenda ★ Architecture ★ Kotlin ★ Recap
  5. 5. Clean Architecture
  6. 6. Persistance FW View Network LocationFW Presenter Entity Gateway Clean Architecture Interactor Entity
  7. 7. Dependency Inversion Principle High Level Low LevelAbstraction Low Level
  8. 8. #AdvArchMobile Respectful Criticism about some Opinionated Decisions
  9. 9. #AdvArchMobile Architecture Components ★ Awesome starting point ★ View Models (inner layer) depend on the SDK:AndroidViewModel (outer layer)
  10. 10. Kotlin
  11. 11. #AdvArchMobile Kotlin ★ Conciseness ★ Data Classes ★ Extensions ★ Property Delegation ★ Sealed Classes
  12. 12. Conciseness
  13. 13. #AdvArchMobile Conciseness (code) @Module public class ProgrammersListModule {
 private ProgrammersListActivity activity;
 
 public ProgrammersListModule(ProgrammersListActivity activity) { this.activity = activity; }
 
 @Provides ProgrammersListPresenter provideProgrammersListPresenter(ShowProgrammersListUseCase useCase) {
 ProgrammersListPresenter presenter = new ProgrammersListPresenter(useCase);
 useCase.setPresenter(presenter);
 presenter.setView(activity);
 return presenter;
 }
 
 @Provides ProgrammersListConnector provideProgrammersListConnector() {
 return new ProgrammersListConnector(activity);
 }
 }
  14. 14. #AdvArchMobile @Module class ProgrammersListModule(private val activity: ProgrammersListActivity) {
 @Provides fun provideProgrammersListPresenter(useCase: ShowProgrammersListUseCase): ProgrammersListPresenter =
 ProgrammersListPresenter(useCase = useCase).apply {
 useCase.presenter = this
 view = activity
 }
 
 @Provides fun provideProgrammersListConnector() = ProgrammersListConnector(view = activity)
 } Conciseness (code) @Module public class ProgrammersListModule {
 private ProgrammersListActivity activity;
 
 public ProgrammersListModule(ProgrammersListActivity activity) { this.activity = activity; }
 
 @Provides ProgrammersListPresenter provideProgrammersListPresenter(ShowProgrammersListUseCase useCase) {
 ProgrammersListPresenter presenter = new ProgrammersListPresenter(useCase);
 useCase.setPresenter(presenter);
 presenter.setView(activity);
 return presenter;
 }
 
 @Provides ProgrammersListConnector provideProgrammersListConnector() {
 return new ProgrammersListConnector(activity);
 }
 }
  15. 15. #AdvArchMobile @Module class ProgrammersListModule(private val activity: ProgrammersListActivity) {
 @Provides fun provideProgrammersListPresenter(useCase: ShowProgrammersListUseCase): ProgrammersListPresenter =
 ProgrammersListPresenter(useCase = useCase).apply {
 useCase.presenter = this
 view = activity
 }
 
 @Provides fun provideProgrammersListConnector() = ProgrammersListConnector(view = activity)
 } Conciseness (code)
  16. 16. #AdvArchMobile Conciseness (test) @Test public void itemIsConfiguredWithNameOfFetchedProgrammer() {
 ProgrammersListItemView item = Mockito.mock(ProgrammersListItemView.class);
 List<ProgrammerResponse> data = new ArrayList<ProgrammerResponse>() {{
 add(TestUtils.createMainProgrammerResponse());
 add(TestUtils.createAltProgrammerResponse());
 }};
 ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
 sut.presentProgrammers(data);
 sut.configureItem(item, 1);
 verify(item).displayName(captor.capture());
 assertEquals(TestData.programmerAltFullName, captor.getValue());
 }
  17. 17. #AdvArchMobile @Test fun itemIsConfiguredWithNameOfFetchedProgrammer() {
 val item = mock<ProgrammersListItemView>()
 val data = listOf(TestData.createMainProgrammerResponse(),
 TestData.createAltProgrammerResponse())
 sut.present(programmers = data)
 
 sut.configureItem(item, 1)
 
 argumentCaptor<String>().apply {
 verify(item).displayName(capture())
 assertEquals(TestData.altFullName, lastValue)
 }
 } Conciseness @Test public void itemIsConfiguredWithNameOfFetchedProgrammer() {
 ProgrammersListItemView item = Mockito.mock(ProgrammersListItemView.class);
 List<ProgrammerResponse> data = new ArrayList<ProgrammerResponse>() {{
 add(TestUtils.createMainProgrammerResponse());
 add(TestUtils.createAltProgrammerResponse());
 }};
 ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
 sut.presentProgrammers(data);
 sut.configureItem(item, 1);
 verify(item).displayName(captor.capture());
 assertEquals(TestData.programmerAltFullName, captor.getValue());
 }
  18. 18. #AdvArchMobile @Test fun itemIsConfiguredWithNameOfFetchedProgrammer() {
 val item = mock<ProgrammersListItemView>()
 val data = listOf(TestData.createMainProgrammerResponse(),
 TestData.createAltProgrammerResponse())
 sut.present(programmers = data)
 
 sut.configureItem(item, 1)
 
 argumentCaptor<String>().apply {
 verify(item).displayName(capture())
 assertEquals(TestData.altFullName, lastValue)
 }
 } Conciseness
  19. 19. Data Classes
  20. 20. #AdvArchMobile Entities data class Programmer( val firstName: String, val lastName: String, val emacs: Int, val caffeine: Int,
 val realProgrammerRating: Int, val interviewDate: Date, val favorite: Boolean) {
 val fullName: String
 get() = "$firstName $lastName"
 }
  21. 21. #AdvArchMobile But ★ Not a value type ★ No defensive copy
  22. 22. #AdvArchMobile Defensive Copying data class Entity private constructor(var private _date: Date) { companion object { fun create(date: Date): Entity = Entity(_date = Date(date.time)) } var date: Date = Date(_date.time) get() = Date(field.time) set(value) { field = Date(date.time) } }
  23. 23. Extensions
  24. 24. #AdvArchMobile Presentation Logic fun Date.relativeDateFormat(origin: Date = Date()): String {
 fun differenceInDays(date1: Date, date2: Date): Long {
 val MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000
 return (date1.time - date2.time) / MILLISECONDS_IN_A_DAY
 }
 
 val daysAgo = differenceInDays(origin, this)
 return when {
 daysAgo < 0 -> "In the future"
 daysAgo < 1 -> "Today"
 daysAgo < 7 -> "Less than a week ago"
 daysAgo < 30 -> "Less than a month ago"
 daysAgo < 365 -> "Less than a year ago"
 else -> "Long time ago"
 }
 }
  25. 25. #AdvArchMobile But ★ A class cannot implement an interface using extensions ★ Methods are declared independently
  26. 26. Property Delegation
  27. 27. #AdvArchMobile Observation data class ProductRequest(var name: String, var units: Int) class Presenter() { var request: ProductRequest by Delegates.observable(ProductRequest(name="", units=0)) { prop, old, new -> requestChanged() } fun requestChanged() { print("Request: $request”) } fun changeDoesNotTrigger() { request.name = "Something" request.units = 1 } fun completeChangeThatTriggers() { request = ProductRequest(name="Else", units= 2) } }
  28. 28. #AdvArchMobile Delegating Reference Type class WeakReferenceHolder<T, U> {
 private var propertyRef: WeakReference<U>? = null
 
 operator fun getValue(t: T, property: KProperty<*>): U? = propertyRef?.get()
 
 operator fun setValue(t: T, property: KProperty<*>, newValue: U?) {
 propertyRef = if (newValue != null) {
 WeakReference(newValue)
 } else {
 null
 }
 }
 }
 var view: ProgrammersListView? by WeakReferenceHolder<ProgrammersListPresenter, ProgrammersListView>()
  29. 29. Sealed Classes
  30. 30. Enum considered harmful
  31. 31. #AdvArchMobile Avoid Enums ★ Space ★ Performance Sealed Classes Too!
  32. 32. #AdvArchMobile Don’t* sealed class Interactor { class ShowProducts(val completion: ()- >Unit): Interactor() {} class DeleteProduct(id: String, completion: ()->Unit) {} } when (interactor) { is ShowProducts -> … }
  33. 33. #AdvArchMobile Do sealed class Result<V: Any, E: Exception> { class Success<V: Any, E: Exception>(val value: V) : Result<V, E>() {//…} class Failure<V: Any, E: Exception>(val error: E) : Result<V, E>() {//…} }
  34. 34. Recap
  35. 35. #AdvArchMobile Recap ★ Kotlin makes advanced architectures easier ★ Learn your options and choose ★ Still learning the idioms, work with the community
  36. 36. Thank You!
  37. 37. @jdortiz #AdvArchMobile

×