Implementing the Observer Pattern in a Kotlin Application

Ricardo Mello
Javarevisited
Published in
5 min readApr 29, 2023

Discover more on YouTube: If you’re looking for video content on technology, programming, and other topics, check out my YouTube channel! Subscribe to stay updated and learn more about these subjects.

In this article, we will explore the Observer Pattern, a design pattern commonly used in software development. Our focus will be to provide a simple explanation of how this pattern works by creating a Kotlin application that simulates the behavior of this pattern.

What’s The Observer Pattern?

The Observer pattern establishes a one-to-many relationship between a subject and a group of observers, where the observers are interested in the state of the subject. For instance, consider a news publisher and a group of subscribers who want to receive news updates. In this scenario, the subscribers can subscribe to a channel and receive updates whenever the publisher releases new content.

The Observer pattern is a useful tool in distributed systems where a single service needs to notify multiple clients about changes. By decoupling the subject and observers, the Observer pattern enables greater flexibility and modularity in system design.

Application Sample

To accomplish our goal, we’ll create a Kotlin application called football-news that allows fans to subscribe, unsubscribe, and receive updates on their favorite teams. By building this application, we’ll demonstrate how the Observer pattern can be used to create a flexible and efficient notification system:

Application Sample

Application Diagram

Application Diagram

As you can see in the diagram above, the Fan class implements the Observer interface called NewsObserver, which is stored as a list of observers in the NewsPublisher.

Hands On

To begin our project, we’ll define the behavior of any observer in the news system using the NewsObserver interface. This interface will specify the methods that all observers should implement to receive news updates. By defining this interface, we’ll ensure that all observers have the same set of methods, making it easy to manage and notify them of changes in the system:

interface NewsObserver {
fun notify(team: Team, message: String)
}

Time to create a Team enum to represent our amazing clubs. And then, let’s bring on the fun by creating our Fan class to implement the NewsObserver interface. Let’s do this:

  • Team

enum class Team {
CORINTHIANS,
LIVERPOOL
}
  • Fan
data class Fan(
private val name: String
) : NewsObserver {
override fun notify(team: Team, message: String) {
println("Hello $name, please check the following news about $team: \n$message\n")
}
}

Now, Let’s define a NewsPublisher that represents the publisher that sends news updates to the subscribed observers:


class NewsPublisher(private val team: Team) {
private val observers: MutableMap<Team, MutableList<NewsObserver>> = mutableMapOf(team to mutableListOf())

fun subscribe(observer: NewsObserver) {
observers.getOrPut(team) { mutableListOf() }.add(observer)
}

fun unsubscribe(observer: NewsObserver) {
observers[team]?.remove(observer)
}

fun notify(message: String) {
observers[team]?.forEach { observer ->
observer.notify(team, message)
}
}
}

Let’s analyze the code above:

  • NewsPublisher class takes in a Team parameter upon instantiation. It also has a mutable map called “observers” that stores a list of NewsObservers for each team
  • subscribe method adds a new observer to the list of observers for the given team. If the team does not exist in the map yet, a new entry is created with an empty list of observers.
  • unsubscribe method removes an observer from the list of observers for the given team.
  • notify method sends a message to all observers of the given team. It retrieves the list of observers for the team from the map and calls the notify method of each observer, passing the team and the message as parameters.

Hooray, we’re making progress 😍! Now, let’s create a NewsSystemBuilder which is a creational design (builder)pattern, which allows constructing complex objects step by step.

package util

import model.Fan
import publisher.NewsPublisher
import model.Team

class NewsSystemBuilder(team: Team) {
private val newsPublisher: NewsPublisher = NewsPublisher(team)

fun subscribe(fan: Fan) = apply { newsPublisher.subscribe(fan) }

fun unsubscribe(fan: Fan) = apply { newsPublisher.unsubscribe(fan) }

fun notify(message: String) = apply { newsPublisher.notify(message) }

companion object {
fun forTeam(team: Team) = NewsSystemBuilder(team)
}
}

Very good! Now we can create our main method:

import model.Fan
import model.Team
import util.NewsSystemBuilder

fun main() {

val ricardo = Fan("Ricardo")
val maria = Fan("Maria")

NewsSystemBuilder.forTeam(Team.CORINTHIANS)
.subscribe(ricardo)
.subscribe(maria)
.notify("Corinthians has won the FIFA World Cup 2 times.")
.notify("Great news! Roger Guedes scored a hat trick against Palmeiras!")
.unsubscribe(ricardo)
.notify("Corinthians will face Flamengo in the COPA DO BRASIL semi-finals.")

NewsSystemBuilder.forTeam(Team.LIVERPOOL)
.subscribe(ricardo)
.notify("Luis Diaz is set to return to Liverpool team training this week having been sidelined since October with a knee injury")
}

Let’s understand the above code:

  • Ricardo and Maria were subscribed to Corinthians Team
  • Both will receive the first two news
  • Ricardo will have his subscription to Corinthians’ news canceled and will not receive the last news about the Flamengo game
  • Later, Ricardo will be subscribed to Liverpool’s news, and only him will receive news about the English club.

Checking the results:

Conclusion

The Observer pattern is a powerful design pattern that can be applied to a wide range of scenarios, from pizza ordering systems to video game character progress tracking, and even to sports news systems.

By using Kotlin’s simple and concise syntax, we can easily implement the Observer pattern and create more flexible, maintainable, and scalable systems. Whether we’re notifying fans about their favorite sports team’s achievements or delivering hot and fresh pizza to our customers 😍

I suggest refactoring-guru for you to delve deeper and study these patterns to obtain more information.

Please feel free to suggest, comment, and contribute to this project. You can find the complete source code on my GitHub page.

Thanks ❤️

--

--

Ricardo Mello
Javarevisited

Senior Software Engineer, member of the MongoDB Community Creator passionate about travel and football. Oracle Certified Associate, Java SE 8 Programmer