DEV Community

liawsy
liawsy

Posted on

The Observer Pattern: A Solution for Subscription-Reliant Applications

Introduction

Suppose you have just ordered a meal at a food place with a 4.8 stars rating on Google. The cashier tells you the order might take awhile, and passes you a buzzer, which will vibrate and sound when your food is ready. As the food place is situated within the mall, you decided to walk around first while waiting for your food.

Diagram depicting scenario. Images from flaticon.com
Diagram depicting above scenario. Images from flaticon.com

With the buzzer, instead of having to constantly check whether your order is ready and staying in the restaurant, you can now return back only when the buzzer sounds.

The scenario above is somewhat similar to the observer pattern—the customer is notified when the restaurant is ready to serve the food, instead of having to constantly check on the restaurant. We'll elaborate more on this later.

So What is the Observer Pattern?

The Observer Pattern is a design pattern used typically for event-driven software architectures. Specifically, it can be used for objects that depend on another object's state changes.

There are two main components that drive the observer pattern—1) the observer, and 2) the subject.

The observer, as the name suggests, observes a subject and is notified by the subject when a particular event occurs. On the other hand, the subject notifies the observer when a particular event occurs. This can be illustrated by the following UML class diagram, that outlines some basic methods both observers and subjects have:

UML Class Diagram for Observer Pattern
UML Class Diagram for Observer Pattern

The class diagram shows that the subject maintains a list of observers subscribed to it so that it will be able to notify this list of observers when the event occurs. Note that each subject can have many observers, demarcated by the asterisk beside the Observer interface.

When Do We Use the Observer Pattern?

The main rationale behind using the Observer pattern is to allow for multiple observers to subscribe to a particular subject. Examples include when multiple UI components are listening for user action, or are waiting for some data to be fetched by the subject in order for them to perform some computation accordingly.

To mitigate the effect of tight coupling, an Observer interface is created so that the Subject does not need to manage each concrete Observer individually.

A specific instance when the Observer pattern is used could be the food collection system in a restaurant, in the example raised earlier:

UML Class Diagram for Food Collection System using Observer Pattern
UML Class Diagram for Food Collection System using Observer Pattern

The Buzzer class implements the Observer class, and subscribes to the OrderHandler class. The OrderHandler class would implement the Subject class, which notifies all subscribers when a particular order is ready. We can also consider another Observer, the WaitStaff class, which models wait staff who will be notified when dine-in orders are ready to be served to customer. In this case, we have multiple Observers subscribing to the OrderHandler class.

Why Use the Observer Pattern?

One benefit of using the Observer Pattern is potentially the improved reactivity of your application. Instead of constantly checking on the subject, or being blocked by waiting on the event to occur on the subject, the observer gets notified only when the event occurs, and can free up its resources to continue with other work instead of constantly checking on the subject.

In the analogy given earlier, with the buzzer that notifies the customer, the customer doesn't have to constantly check for the order and is able to do other things.

You could for example, set the observer to listen for the event outside of the main thread, and do other work on the main thread. In observer pattern libraries like ReactiveX, the observer can observe the subject on a specified thread, which prevents blocking of other processes that need to run on the main thread [1].

Real-Life Applications of the Observer Pattern in Android

During my internship as an Android engineer at Ninja Van, I found myself having to use the Observer pattern in various aspects of Android development — from observing the UI for changes, to observing the database for any output to update the UI.

The team made use of Android's in-build LiveData class, which loosely help developers to implement the Observer pattern. The LiveData itself is an observable data holder class [2], similar to a Subject, and can be bounded to UI elements, which are the Observers will update themselves on LiveData updates and notification.

The use of the Observer pattern is particularly important in Android. This is because the main thread is used for rendering UI [3], so actions such as synchronously accessing the database should be avoided, and instead we can get the UI to observe the ViewModel for any data retrieval event, so that we could update the UI accordingly.

There are also other libraries supporting the Observer pattern, such as ReactiveX, which is also used by my team to better support the subscription needs of various classes. It is also used to notify UI components when data is being retrieved from persistence via a non-UI thread. However, the use cases of ReactiveX span beyond the Observer pattern, which is perhaps a topic for another day.

The Observer pattern can be incredibly useful for MV* architectures, where UI components are heavily dependent on the data. But note the risks that come with it, so use it wisely!

References

[1] ReactiveX - Scheduler: http://reactivex.io/documentation/scheduler.html
[2] LiveData Overview: https://developer.android.com/topic/libraries/architecture/livedata
[3] Processes and threads Overview: https://developer.android.com/guide/components/processes-and-threads
Others:
Observer: https://refactoring.guru/design-patterns/observer

Discussion (2)

Collapse
jasaaan profile image
Jason Lim

Thanks for the great write up! Found it extremely easy to follow, and I like how your post helped me understand with very relatable real life examples, plus the addition of UML diagrams (instead of just a chunks of codes) to aid my understanding. It'd be nice to know more about the pros & cons so I know what to look out for when implementing the pattern in future!

You mentioned that one of the reasons for using the Observer pattern is that it makes the app more reactive - instead of leaving the app in a blocked state or constantly polling. I also wanted to add on another benefit which is the fact that it follows the open-closed principle (a class should be open for extensions without having to change the class). In the Observer pattern, we can add as many observers as we want without having to change the code. I have also played around with some libraries supporting the Observer pattern e.g. react-intersection-observer and the Observer pattern is indeed widely used in UI rendering and responding to certain events.

Also, I used to be really confused between the Publisher-Subscriber pattern (pub-sub) and the Observer pattern. I always thought they were the same thing. Here is a quick summary that might help anyone also confused between the two:

In the Observer pattern, all Observers are aware of the Subject and the Subject also keeps a list of Observers. The Subject notifies all Observers in response to certain events.
In a Publisher-Subscriber pattern, similarly, the Subject (Publisher) notifies the Observers (Subscribers). However, there is a component in-between called a message broker which is known by both the publisher and subscriber - its job is to filter the messages and dispatch them accordingly.
They both look quite similar at first glance. The key difference would be that in a pub-sub pattern, the publisher and subscribers are not aware about the existence of each other whereas in the Observer pattern, the Subject has a list of Observers at hand. Hence, the pub-sub pattern is also loosely coupled.

Thanks again for the informative post! Was also nice to hear about your experience with the Observer pattern during your internship - proving it's useful and still widely used!

Collapse
artlee06 profile image
Arthur Lee Ying Kiu

Hi there! Great job with this post! I really liked how you used an analogy at the start with the restaurant and the buzzer to explain the observer pattern as it has a direct mapping with how the observer pattern actually works. I also really liked how you related the pattern to your previous internship at NinjaVan doing Android development.

The content and flow is quite complete, perhaps since you have a section that says "When Do We Use the Observer Pattern?", it would be beneficial to have a section called "When not to use the observer pattern" that has specific examples of when this pattern would not be useful or even be a detriment to the software being developed.

Upon looking closer, I realised that reactiveX that you mentioned provides API for async programming with observables also works with RxJs. This relates to my previous internship at StaffAny where we used redux-observable which is an RxJS based middleware for redux. It allowed us to create side effects on our frontend based on specific responses received from our backend API calls. It uses Epics to listen for specific actions and trigger side effects written in the main logic of the pic. This allows us to do fancier interactions based on the backend response, such as making different modals open or close with a time delay based on the backend response as a side-effect to the main interaction.

Overall great job! Your post helped to solidify my understanding of such a pattern and relate it to my own experiences as well.