Observer Pattern

The Observer pattern is one of the behavioral software patterns, described in the book of the Gang-Of-Four. It is mainly used to implement distributed event handling systems, in “event driven” software.

The Observer pattern addresses the following problems:

  • A one-to-many dependency between objects should be defined without making the objects tightly coupled.
  • It should be ensured that when one object changes state an open-ended number of dependent objects are updated automatically.
  • It should be possible that one object can notify an open-ended number of other objects.

Our example scenario

We are having two classes: the WeatherStation and WeatherApp Class. The WeatherApp is showing the temperature to its users. To work properly it needs to know when the temperature changes.

There are two possible ways how the WeatherApp gets the temperature.

Option 1 – WeatherApp ask WeatherStation

In this option the WeatherApp has a dependency to the WeatherStation and ask itself for the temperature. In this case the WeatherStation is coupled to the WeatherApp, but not vice-versa.

class WeatherStation{
    var temperature = 0
}

class WeatherApp(var weatherStation : WeatherStation){
    private fun show(temperature: Int){
        println("New Temperature in App: $temperature")
    }

    fun update(){
        show(weatherStation.temperature)
    }
}

Option 2 – WeatherApp gets triggered

In this option some class triggers a function of the WeatherApp and provides the new temperature.

class WeatherStation{
    var temperature = 0
}

class WeatherApp{
    private fun show(temperature: Int){
        println("New Temperature in App: $temperature")
    }
    
    fun update(temperature: Int){
        show(temperature)
    }
}

class OtherClass(private var mobileApp: WeatherApp, 
                 private var weatherStation : WeatherStation){
    fun update(){
        mobileApp.update(weatherStation.temperature)
    }
}

From the WeatherApp and WeatherStation perspective this approach is quite good, because they are not at all coupled. None of these classes know about the existence of the other one. Of course, the OtherClass will know about both.

Both approaches however have a big flaw. How often does the WeatherApp need to be updated so that it will work? Every hour? Every minute? Every second? It is obvious that this is a big drawback which could lead to big performance issues.

In the simplest case this problem can be resolved, when the dependency from Option 1 is reversed. Now the WeatherStation knows about WeatherApp and can notify it when the temperature changes. We are having now a Pull instead of a Push situation.

class WeatherStation(private var mobileApp : MobileApp){
    var temperature = 0
        set(value) {
            field = value
            mobileApp.update(temperature)
        }
}

class MobileApp(){
    private fun show(temperature: Int){
        println("New Temperature in App: $temperature")
    }

    fun update(temperature: Int){
        show(temperature)
    }
}

This solution solves the problem about “when” is the WeatherApp notified. However, it brings other problems. The dependency is questionable. What happens if other objects (maybe from other classes) need as well to be notified when the temperature changes. In a long shot this system is not sustainable.

We need a solution which is more generic and flexible. At this point the Observer pattern comes into the play.

UML Diagram Observer Pattern

Observer pattern uml diagram

We are going to implement it in 3 ways.

  1. Standart Observer Pattern implementation
  2. Signal – Slot mechanis
  3. Kotlin Built – In Observable Delegates

Standard Oberserver Pattern Implementation

In this implementation we are following the design from Gang Of Four and implement two interfaces. The first interface IObserver defines the update function. The WeatherApp will implement this interface.

The second interface IObservable defines how to add and remove IObservers and how to notify them. Thanks to Kotlin, this interface has a default implementation. The WeatherStation is using this interface.

Source Code

The interfaces

interface IObserver{
    fun update()
}
interface IObservable{
    val observers : ArrayList<IObserver>
    fun add(observer : IObserver){
        observers.add(observer)
    }
    fun remove(observer : IObserver){
        observers.remove(observer)
    }
    fun dataChanged(){
        observers.forEach { it.update() }
    }
}

The WeatherStation Class

class WeatherStation : IObservable {
    override val observers: ArrayList<IObserver> = ArrayList()
    var temperature = 0
        set(value) {
            field = value
            dataChanged()
        }
}

The WeatherApp Class

The WeatherApp has still a dependency to the WeatherStation, because it gets only notified that the state of the station changed. So, it must request the data from it actively.

class WeatherApp (private var station: WeatherStation) : IObserver {
    override fun update() {
        show(station.temperature)
    }

    private fun show(temperature: Int){
        println("New Temperature in App: $temperature")
    }
}

Signal and Slot mechanism

A special implementation of the observer pattern is the signal and slot mechanism. Signals and slots are used for communication between objects, mostly used in UI frameworks. In the example above we could implement signals for each property. The WeatherStation sends a signal when its temperature changes. The good thing about that design is, that the temperature is directly send via the signal. The WeatherApp, which is connected to the signal, does not need to ask the WeatherStation. This means that both classes are completely decoupled.

The signal class

A specialized class which keeps count which function needs to execute on emit.

class Signal<TType> {
    private val callbacks = mutableListOf<(TType) -> Unit>()

    fun connect(slot: (TType) -> Unit) {
        callbacks.add(slot)
    }

    fun emit(value: TType) {
        callbacks.forEach{it(value)}
    }
}

The WeatherStation

Implements the signal and triggers it on properties set methods.

class WeatherStation{
    var temperature = 0
        set(value) {
            field = value
            temperatureChanged.emit(temperature)
        }
    val temperatureChanged = Signal<Int>()
}

The WeatherApp Class

An application which shows the temperature when it changes. Note that in this case the show function is actually not a function but a functor variable.

class WeatherApp(station: WeatherStation) {
    private var show : (Int) -> Unit = { temperature ->
        println("Temperature changed: $temperature")
        println(this)
    }

    init {
        station.temperatureChanged.connect(show)
    }
}

Kotlin’s Built-in Observable Delegates

Another option (and probably the preferred one) is to use the Kotlin Observable delegate. This returns a property delegate for a read/write property that calls a specified callback function when changed. The callback takes 3 input arguments: property, oldValue and newValue.

inline fun <T> observable(
    initialValue: T,
    crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit
): ReadWriteProperty<Any?, T>

In our example we are only interested in the new value. The modified WeatherStation has a list of lambda functions in its member variable temperatureChanged. One can simply add new functions to this variable. In this way the WeatherStation combines the Signal<TType> class of the Signal Slot example.

class WeatherStation {
    val temperatureChanged = mutableListOf<(Int) -> Unit>()

    var temperature: Int by Delegates.observable(0) { _, _, newValue ->
        temperatureChanged.forEach{it(newValue)}
    }
}

val weatherStation = WeatherStation()    
weatherStation.temperatureChanged.add { temperature ->
    println("Temperature changed: $temperature")
}

As with the Signal-Slot mechanism, the WeatherApp and WeatherStation are completely decoupled. None knows about the existence of the other one. It must be part of another class / function to connect the WeatherApp to signal of the WeatherStation.

Android Observable Example

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/title"
            android:textSize="24dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="50dp"
            android:text="Android Observable Example" />

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:layout_gravity="center"
            android:text="Click Me" />

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:layout_gravity="center"
            android:text="" />
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Android Observable Example
package com.example.observerexample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var button = findViewById<Button>(R.id.button)
        var textView = findViewById<TextView>(R.id.text)
        button.setOnClickListener { textView.setText("Button was clicked!") }
    }
}

Conclusion

Three ways are shown to implement the Observer Pattern in Kotlin. Thankfully Kotlin has implemented this pattern nativly implemented via its Observable Delegate properties.
Using the Observable from Kotlin will allow you to have communication between objects which are still decoupled from each other.