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

Architecture Patterns in Practice with Kotlin

Karumi
November 25, 2017

Architecture Patterns in Practice with Kotlin

Kotlin is here and some Architecture Patterns need to be adapted.

From Karumi we've dedicated last 3 years talking about architecture and design patterns, each solution has pros and cons and could be good for different problems and kind of applications.

Kotlin brings to us different tools and ways to write code these can help us to write different patterns or apply different technics to that patterns.

In this talk, we present some commons problems in app designs and how we can provide solutions in Kotlin.

Karumi

November 25, 2017
Tweet

More Decks by Karumi

Other Decks in Programming

Transcript

  1. Testing for android & iOS. Trainings Architecture, Patterns and principles

    for Android & iOS. Mastering Git. Advanced mobile development. Testing for android & iOS. Architecture, Patterns and principles for Android & iOS. Companies For Everybody
  2. Sergio Gutierrez Senior Full Stack Engineer Pedro Gomez Senior Full

    Stack Engineer Davide Mendolia Senior Full Stack Engineer Fran Toro Senior Mobile Engineer
  3. Adam Tornhill “Computer languages differ not so much in what

    they make possible, but in what they make easy.” Larry Wall
  4. Why

  5. Activity Fragment Service View … Presenter View Use Case Rich

    Model Repository DataSource Api Retrofit SharedPref Api Sync Execution
  6. Adam Tornhill class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val

    presenter: SuperHeroesPresenter by injector.instance() private lateinit var adapter: SuperHeroesAdapter override val layoutId: Int = R.layout.main_activity override val toolbarView: Toolbar get() = toolbar /*…*/ }
  7. Adam Tornhill class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val

    presenter: SuperHeroesPresenter by injector.instance() private lateinit var adapter: SuperHeroesAdapter override val layoutId: Int = R.layout.main_activity override val toolbarView: Toolbar get() = toolbar /*…*/ } Computed properties
  8. Adam Tornhill abstract class BaseActivity : KodeinAppCompatActivity(), LifecyclePublisher by lifeCycleLinker

    { abstract val layoutId: Int abstract val presenter: LifecycleSubscriber abstract val toolbarView: Toolbar abstract val activityModules: Module /*…*/ }
  9. Adam Tornhill class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val

    presenter: SuperHeroesPresenter by injector.instance() private lateinit var adapter: SuperHeroesAdapter override val layoutId: Int = R.layout.main_activity override val toolbarView: Toolbar get() = toolbar /*…*/ } override properties
  10. Adam Tornhill class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val

    presenter: SuperHeroesPresenter by injector.instance() private lateinit var adapter: SuperHeroesAdapter override val layoutId: Int = R.layout.main_activity override val toolbarView: Toolbar get() = toolbar /*…*/ }
  11. Adam Tornhill abstract class BaseActivity : KodeinAppCompatActivity(), LifecyclePublisher by lifeCycleLinker

    { abstract val layoutId: Int abstract val presenter: LifecycleSubscriber abstract val toolbarView: Toolbar abstract val activityModules: Module /*…*/ } Delegate
  12. Adam Tornhill abstract class BaseActivity : KodeinAppCompatActivity(), LifecyclePublisher by lifeCycleLinker

    { abstract val presenter: LifecycleSubscriber /*…*/ override fun onResume() { super.onResume() update() } override fun onDestroy() { unregister(presenter) super.onDestroy() } delegate to..
  13. LifecycleLinker : LifecyclePublisher private val receivers = ArrayList<LifecycleSubscriber>() override fun

    unregister (subscriber: LifecycleSubscriber) { receivers.remove(subscriber) } override fun update() { receivers .forEach (LifecycleSubscriber::update) } /*…*/ } Adam Tornhill override fun onResume() { super.onResume() update() } override fun onDestroy() { unregister(presenter) super.onDestroy() } BaseActivity : LifecyclePublisher by lifeCycleLinker
  14. Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes)

    :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ }
  15. Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes)

    :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ } argument properties
  16. Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes)

    :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ } argument properties delegate property
  17. fun <T> weak(value: T) = WeakRef(value) class WeakRef<out T>(value: T)

    { private val weakReference: WeakReference<T> = WeakReference(value) operator fun getValue(thisRef: Any, property: KProperty<*>): T? = weakReference.get() } Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ } argument poperties
  18. fun <T> weak(value: T) = WeakRef(value) class WeakRef<out T>(value: T)

    { private val weakReference: WeakReference<T> = WeakReference(value) operator fun getValue(thisRef: Any, property: KProperty<*>): T? = weakReference.get() } Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ }
  19. class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) : LifecycleSubscriber

    { private val view: View? by weak(view) override fun update() { view?.showLoading() refreshSuperHeroes() } We’re acting (Optional)
  20. fun <T> weak(value: T) = WeakRef(value) class WeakRef<out T>(value: T)

    { private val weakReference: WeakReference<T> = WeakReference(value) operator fun getValue(thisRef: Any, property: KProperty<*>): T? = weakReference.get() } Adam Tornhill class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) :LifecycleSubscriber { private val view: View? by weak(view) /*…*/ }
  21. class NetworkSuperHeroDataSource( private val apiClient: CharacterApiClient) : SuperHeroDataSource { override

    fun get(key: String): Either<DomainError, SuperHero> = try { mapResponse(apiClient.getCharacter(key)) { mapSuperHero(it) } } catch (exception: Exception) { Either.left(mapException(exception)) } /*…*/ } Adam Tornhill
  22. class NetworkSuperHeroDataSource( private val apiClient: CharacterApiClient) : SuperHeroDataSource { override

    fun get(key: String): Either<DomainError, SuperHero> = try { mapResponse(apiClient.getCharacter(key)) { mapSuperHero(it) } } catch (exception: Exception) { Either.left(mapException(exception)) } /*…*/ } Adam Tornhill
  23. class NetworkSuperHeroDataSource( private val apiClient: CharacterApiClient) : SuperHeroDataSource { override

    fun get(key: String): Either<DomainError, SuperHero> = try { mapResponse(apiClient.getCharacter(key)) { mapSuperHero(it) } } catch (exception: Exception) { Either.left(mapException(exception)) } /*…*/ } Adam Tornhill
  24. GetSuperHeroes: fun invoke(): Either<DomainError, List<SuperHero>> SuperHeroesRepository: fun getAllSuperHeroes(): Either<DomainError, List<SuperHero>>

    SuperHeroDataSource: fun getAll(): Either<DomainError, List<SuperHero>> Adam Tornhill Don’t break the chain
  25. private fun refreshSuperHeroes() = async { val result = await

    { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } Adam Tornhill MainPresenter: switch on steroids
  26. private fun refreshSuperHeroes() = async { val result = await

    { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } Adam Tornhill MainPresenter: smart cast
  27. abstract class BaseActivity : KodeinAppCompatActivity(), LifecyclePublisher by lifeCycleLinker { /*…*/

    fun showError(error: DomainError) = Snackbar.make(toolbarView, error.asString(this), Snackbar.LENGTH_LONG).show() } Adam Tornhill
  28. abstract class BaseActivity : KodeinAppCompatActivity(), LifecyclePublisher by lifeCycleLinker { /*…*/

    fun showError(error: DomainError) = Snackbar.make(toolbarView, error.asString(this), Snackbar.LENGTH_LONG).show() } Adam Tornhill
  29. fun DomainError.asString(context: Context): String = context.getString(getMessage(this)) fun getMessage(domainError: DomainError): Int

    = when (domainError) { is NotInternetDomainError -> R.string.error_not_internet_message is NotIndexFoundDomainError -> R.string.error_superhero_not_found_message is AuthDomainError -> R.string.error_invalid_credentials is UnknownDomainError -> R.string.error_unknown_message } Adam Tornhill extension method
  30. fun DomainError.asString(context: Context): String = context.getString(getMessage(this)) fun getMessage(domainError: DomainError): Int

    = when (domainError) { is NotInternetDomainError -> R.string.error_not_internet_message is NotIndexFoundDomainError -> R.string.error_superhero_not_found_message is AuthDomainError -> R.string.error_invalid_credentials is UnknownDomainError -> R.string.error_unknown_message } Adam Tornhill
  31. fun DomainError.asString(context: Context): String = context.getString(getMessage(this)) fun getMessage(domainError: DomainError): Int

    = when (domainError) { is NotInternetDomainError -> R.string.error_not_internet_message is NotIndexFoundDomainError -> R.string.error_superhero_not_found_message is AuthDomainError -> R.string.error_invalid_credentials is UnknownDomainError -> R.string.error_unknown_message } Adam Tornhill Compile error!!
  32. sealed class DomainError object NotInternetDomainError : DomainError() data class UnknownDomainError(

    val errorMessage: String = "Unknown Error") : DomainError() data class NotIndexFoundDomainError(val key: String) : DomainError() object AuthDomainError : DomainError() Adam Tornhill
  33. class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) : LifecycleSubscriber

    { private fun refreshSuperHeroes() = async { val result = await { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } /*…*/ } Adam Tornhill is this a method? or is an object?
  34. class GetSuperHeroes( private val superHeroesRepository: SuperHeroRepository) { operator fun invoke():

    Either<DomainError, List<SuperHero>> = superHeroesRepository .getAllSuperHeroes() } Adam Tornhill Override operators getSuperHeroes()
  35. data class SuperHero( val id: String, val name: String, val

    photo: String? = null, val isAvenger: Boolean, val description: String) Adam Tornhill
  36. data class SuperHero( val id: String, val name: String, val

    photo: String? = null, val isAvenger: Boolean, val description: String) Adam Tornhill All domain models in the same file
  37. Activity Fragment Service View … Presenter View Use Case Rich

    Model Repository DataSource Api Retrofit SharedPref Api Sync Execution
  38. class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) : LifecycleSubscriber

    { private fun refreshSuperHeroes() = async { val result = await { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } /*…*/ }
  39. class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) : LifecycleSubscriber

    { private fun refreshSuperHeroes() = async { val result = await { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } /*…*/ }
  40. class SuperHeroesPresenter( view: View, private val getSuperHeroes: GetSuperHeroes) : LifecycleSubscriber

    { private fun refreshSuperHeroes() = async { val result = await { getSuperHeroes() } view?.hideLoading() when (result) { is Right -> showSuperHeroes(result.r) is Left -> view?.showError(result.l) } } /*…*/ }
  41. class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val presenter: SuperHeroesPresenter

    by injector.instance() /*...*/ override val activityModules = Module(allowSilentOverride = true) { bind<SuperHeroesPresenter>() with provider { SuperHeroesPresenter(this@MainActivity, instance()) } bind<GetSuperHeroes>() with provider{ GetSuperHeroes(instance()) } }
  42. class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val presenter: SuperHeroesPresenter

    by injector.instance() /*...*/ override val activityModules = Module(allowSilentOverride = true) { bind<SuperHeroesPresenter>() with provider { SuperHeroesPresenter(this@MainActivity, instance()) } bind<GetSuperHeroes>() with provider{ GetSuperHeroes(instance()) } }
  43. class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val presenter: SuperHeroesPresenter

    by injector.instance() /*...*/ override val activityModules = Module(allowSilentOverride = true) { bind<SuperHeroesPresenter>() with provider { SuperHeroesPresenter(this@MainActivity, instance()) } bind<GetSuperHeroes>() with provider{ GetSuperHeroes(instance()) } }
  44. class MainActivity : BaseActivity(), SuperHeroesPresenter.View { override val presenter: SuperHeroesPresenter

    by injector.instance() /*...*/ override val activityModules = Module(allowSilentOverride = true) { bind<SuperHeroesPresenter>() with provider { SuperHeroesPresenter(this@MainActivity, instance()) } bind<GetSuperHeroes>() with provider{ GetSuperHeroes(instance()) } }
  45. Bibliography They’re the cracks! Clean Code. Uncle Bob. 2008 Kotlin

    For Android Developers. Antonio Leiva. 2015 GitHub AsyncAwait . metalabdesign. Github funKTionale. MarioAriasC. Kodein. Salomon Brys. Karumi Blog and Katas on github.