The Class Delegate Alternative

By using a class delegate, we get the best of both worlds: the FavoriteStore remains internal to the ItemViewModel implementation, yet we get a zero-cost implementation of the FavoriteStore API:

interface FavoriteStore<T> {
  fun isFavorite(thingy: T): Boolean
  
  fun toggleFavorite(thingy: T, isFavorite: Boolean)
}

class InMemoryFavoriteStore<T> : FavoriteStore<T> {
  private val favorites: MutableMap<T, Boolean> = mutableMapOf()
  
  override fun isFavorite(thingy: T) = favorites[thingy] ?: false
  
  override fun toggleFavorite(thingy: T, isFavorite: Boolean) {
    favorites[thingy] = isFavorite
  }
}

data class Item(val id: String, val name: String)

class ItemViewModel(favorites: FavoriteStore<Item>) : FavoriteStore<Item> by favorites

fun main() {
  val vm = ItemViewModel(InMemoryFavoriteStore())
  val item = Item("this is my id", "this is my name")
  
  println("item isFavorite: ${vm.isFavorite(item)}")
  vm.toggleFavorite(item, true)
  println("item isFavorite: ${vm.isFavorite(item)}")
}

When we try using the FavoriteStore API on ItemViewModel, Kotlin forwards those calls along to favorites, without us having to do that delegation manually. Yet, favorites is just a constructor parameter — it is not even a property.

Now, in this case, we could also have ItemViewModel inherit from InMemoryFavoriteStore() and get the same result. However, in a real-world app, we will want to supply different FavoriteStore implementations, perhaps through dependency inversion frameworks. Forcing ItemViewModel to inherit from InMemoryFavoriteStore would eliminate the flexibility to swap in a different implementation for different circumstances.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.