Delegation

Unlike Java, Kotlin makes delegation easy. According to the books Design Patterns, by the Gang of Four, and Effective Java one should "favor composition over inheritance" (Item 6). Kotlin provides class delegation to facilitate the delegation and decorator patterns being one of the first languages to provide native support for these patterns.

The Liskov substitution principle is a concept in OOP stating that all subclasses should act like their superclasses. In easier words, If unit tests are passing for some class, they should be passing for its subclasses too. This principle has been popularized by Robert C. Martin, who placed it in his set of most important OOP rules and described it in the popular book

Clean Code

delegation should be used when:

  • When your subclass violates the Liskov substitution principle; for example, when we are dealing with situations where inheritance was implemented only to reuse code of the superclass, but it is not really acting like it.

  • When the subclass uses only a portion of the methods of the superclass. In this case, it is only a matter of time before someone calls a superclass method that they were not supposed to call. Using delegation, we reuse only methods we choose (defined in the interface).

  • When we cannot or we should not inherit, because:

    • The class is final

    • It is not accessible and used from behind interface

    • It is just not designed for inheritance

The book Effective Java states that "inheritance is appropriate only in circumstances where a subclass really is a subtype of the superclass." In other words, class B should extend a class only if an is-a relationship exists between the two classes. If you are tempted to have class B extend class A, ask yourself Is every B really an A? In every other case there should be composition used (which most common implementation is delegation).

Kotlin uses the keyword by to delegate all methods from an interface to the concrete class that follows.

fun main(args: Array<String>) {
    Greeter("Hello", "John").print()

    ScreamingGreeter("Hi", "Bob").print()
}

interface Printer {
    fun print()
}

class CommandLinePrinter(val msg: String) : Printer {
    override fun print() {
        println(msg)
    }
}

class UpcaseCommandLinePrinter(val msg: String) : Printer {
    override fun print() {
        println(msg.toUpperCase())
    }
}

class Greeter(msg: String, name: String): Printer by CommandLinePrinter("$msg, $name.")

class ScreamingGreeter(msg: String, name: String): Printer by UpcaseCommandLinePrinter("$msg, $name!")

The Observable Delegate

Mutable observation

var name: String by Delegates.observable("Empty"){ 
        property, oldValue, newValue -> // 1 
        println("$oldValue -> $newValue") // 2 
    } 

    // Usage 
    name = "Martin" // 3, 
    Prints: Empty -> Martin 
    name = "Igor" // 3, 
    Prints: Martin -> Igor 
    name = "Igor" // 3, 4 
    Prints: Igor -> Igor

Immutable

class SomeActivity : Activity() { 
        var list: List<String> by Delegates.observable(emptyList()) { 
            prop, old, new -> if(old != new) updateListView(new) 
        }   
        //  ... 
    } 
//or
var list: List<LocalDate> by observable(list) { _, old, new ->  // 1 
  if(new != old) notifyDataSetChanged() 
}

The Veroable Delegate

Allows for a change to be rejected if a given condition is not meet.

var list: List<String> by Delegates.vetoable(emptyList()) 
{ _, old, new ->  
   new.size > old.size 
} 

//for validation
  var name: String by Delegates.vetoable("") 
    { prop, old, new ->  
    if (isValid(new)) { 
        showNewData(new) 
        true 
    } else { 
        showNameError() 
        false 
    }

View Binding for MVP

https://github.com/MarcinMoskala/KotlinAndroidViewBindings

fun Activity.bindToText( 
    @IdRes viewId: Int ) = object : 
    ReadWriteProperty<Any?, String> { 

  val textView by lazy { findViewById<TextView>(viewId) } 

  override fun getValue(thisRef: Any?, 
      property: KProperty<*>): String { 
      return textView.text.toString() 
  } 

  override fun setValue(thisRef: Any?, 
      property: KProperty<*>, value: String) { 
      textView.text = value 
  } 
}

//or 

fun Activity.bindToVisibility( 
   @IdRes viewId: Int ) = object : 
   ReadWriteProperty<Any?, Boolean> { 

   val view by lazy { findViewById(viewId) } 

  override fun getValue(thisRef: Any?, 
      property: KProperty<*>): Boolean { 
      return view.visibility == View.VISIBLE 
  } 

  override fun setValue(thisRef: Any?, 
      property: KProperty<*>, value: Boolean) { 
      view.visibility = if(value) View.VISIBLE else View.GONE 
  } 
}

results matching ""

    No results matching ""