DEV Community

Oluwasanmi Aderibigbe
Oluwasanmi Aderibigbe

Posted on • Updated on

Head First Design Patterns: 1 of 10

I just learnt my first design from The Head First Design Pattern book. Today, I learnt about the Observer pattern.

According to Head First Design Patterns, the Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependent are notified and updated automatically. The object that sends out changes is called the Subject while objects that receive changes are called the Observers.

I also got to the Observer pattern is slightly different from the Publish-Subcribe pattern. In the Observer pattern, there is no middle filtering out the events sent to the observers by the subject. Event are sent directly to observers. This is not the case in the Publish-Subscribe pattern. In the Publish-Subscribe pattern, the event sent by the Publisher(subject) first goes to an Event-bus. The Event-bus then filters and sends the events to subcribers(observers) accordingly.

For example,in order to implement the Observer pattern, you need two interfaces: Subject and Observer.

 interface Subject {
    fun registerObserver(o : Observer)
    fun removeObserver(o: Observer)
    fun  notifyObservers()
}
Enter fullscreen mode Exit fullscreen mode

The Subject interface defines the behaviour for subjects. Subjects can register an observer, remove an observer and notify observers of changes in its state.

interface Observer {
    fun update(title: String, airTime: String)
}
Enter fullscreen mode Exit fullscreen mode

The Object interface defines the behaviour for objects. Object can receive new update of anime with their airing time.

class AnimeSubject : Subject {
    private val observers = mutableListOf<Observer>()
    private var title: String = ""
    private var airTime: String = ""


    override fun registerObserver(o: Observer) {
        observers.add(o)
    }

    override fun removeObserver(o: Observer) {
        observers.remove(o)
    }

    override fun notifyObservers() {
        observers.forEach { observer: Observer ->
            observer.update(title, airTime)
        }
    }

    fun setAnime(title: String, airTime: String) {
        this.title = title
        this.airTime = airTime
        notifyObservers()
    }
}
Enter fullscreen mode Exit fullscreen mode

The AnimeSubject implements the Subject interface. It contains a list of observer. Whenever the AnimeSubject state changes, it notifies its observer of the changes.

class SanmiObserver(val animeSubject: AnimeSubject) : Observer {
    init {
        animeSubject.registerObserver(this)
    }
    override fun update(title: String, airTime: String) {
        println("Thanks for notifying me about $title airing at $airTime")
    }
}
Enter fullscreen mode Exit fullscreen mode

The SanmiObserver implements the Observer interface. It also takes an AnimeSubject as a dependency. Registering itself as an observer of AnimeSubject

class AdeObserver(val animeSubject: AnimeSubject) : Observer {
    init {
        animeSubject.registerObserver(this)
    }
    override fun update(title: String, airTime: String) {
        println("Yo for notifying me about $title airing at $airTime")
    }
}
Enter fullscreen mode Exit fullscreen mode
fun main() {
    val animeSubject = AnimeSubject()

    val sanmiObserver = SanmiObserver(animeSubject)
    val adeObserver = AdeObserver(animeSubject)

    animeSubject.setAnime("Tokyo Revenger", "21:30")
}

prints:
Thanks for notifying me about Tokyo Revenger airing at 21:30
Yo for notifying me about Tokyo Revenger airing at 21:30
Enter fullscreen mode Exit fullscreen mode

Whenever the animeSubject changes its state, its observer will be notified of the changes.

The advantages of using the Observer pattern are

  1. Subjects and observers are loosely coupled so changes in either the subject or observer will not affect each other.This also makes it very easy to define new observers, add and remove observer at any time.
  2. It makes communication between different parts of your software easier.
  3. It also reduces the amount of resources used as you don't have to be pulling for the existence of new information every time because whenever the state of your subject changes, your observers will be notified.

Common use case for the Observer pattern in Android development for example would be, setting onClickListerner, registering for changes of any kind, communication between recyclerviews and fragments/activities.

Discussion (0)