1. Introduction
In this tutorial, we’ll learn about the Observer pattern and take a look at a few different implementations in Kotlin.
2. What Is the Observer Pattern?
The Observer pattern is a behavioral software pattern. It was first described in the Design Patterns book written by the Gang Of Four.
The pattern shows the definition of a subscription mechanism that notifies multiple objects about any changes that happen to the observed object. That’s why the Observer pattern is quite popular in distributed event-driven systems.
For a better understanding, we can consider an example of Baeldung its readers. Let’s say that the readers get a notification every time a new article appears.
In this example, the readers are the Observers and Baeldung is the Observable.
3. Standard Observer Pattern Implementation
As a first implementation, we’re going to follow the design from the Gang Of Four book. It consists of two interfaces.
The first one, the IObserver defines the update action:
interface IObserver {
fun update()
}
This method will execute every time a change happens within the object we’re observing.
The second interface, the IObservable, is responsible for holding the information about all of the observers and sending update events to them:
interface IObservable {
val observers: ArrayList<IObserver>
fun add(observer: IObserver) {
observers.add(observer)
}
fun remove(observer: IObserver) {
observers.remove(observer)
}
fun sendUpdateEvent() {
observers.forEach { it.update() }
}
}
By using the add() and remove() methods, we can keep track of the observers. The sendUpdateEvent() method is responsible for sending the update event to all observers.
In the simplest implementation, it just iterates over all observers and triggers the update() method on each one of them.
Let’s now consider an example implementation of these interfaces.
In effect, we’re going to create a simple newsletter engine that will store the information about the latest Baeldung article. As a result, once a new article appears, it triggers the sendUpdateEvent() method.
We’ll store the observers as a list of IObserver instances and a URL for the newest article as a String:
class BaeldungNewsletter : IObservable {
override val observers: ArrayList<IObserver> = ArrayList()
var newestArticleUrl = ""
set(value) {
field = value
sendUpdateEvent()
}
}
By overriding the setter for the newestArticleUrl property we’re able to execute the update method.
Now, let’s take a look at the observer implementation. For the sake of simplicity we’ll just print the article URL to the console:
class BaeldungReader(private var newsletter: BaeldungNewsletter) : IObserver {
override fun update() {
println("New Baeldung article: ${newsletter.newestArticleUrl}")
}
}
4. Kotlin’s Built-in Observable Delegates
Another way of implementing the Observer pattern is to use Kotlin’s built-in observable delegates. The delegate returns a callback function that executes when a given property is read or written.
In this implementation, the observers are stored as a list of lambda functions. Hence, the article URL will be a String field as before but this time with a Delegate assigned to it:
class BaeldungNewsletter {
val newestArticleObservers = mutableListOf<(String) -> Unit>()
var newestArticleUrl: String by Delegates.observable("") { _, _, newValue ->
newestArticleObservers.forEach { it(newValue) }
}
}
In our example, we listen only to write operations. For this reason, the Delegate has only one parameter defined.
As a result, the newestArticleUrl parameter gets a new value and we pass it to all of the observers.
The observer can have any desired implementation. For the sake of simplicity, we’ll again just print the newest article URL to the console:
val newsletter = BaeldungNewsletter()
newsletter.newestArticleObservers.add { newestArticleUrl ->
println("New Baeldung article: ${newestArticleUrl}")
}
5. Conclusion
In this tutorial, we described two different ways of implementing the Observer design pattern in Kotlin. As usual, the source code with all the examples in this tutorial is available over on GitHub.