The Observer Pattern is a design pattern in software engineering that allows communication between multiple places. In web development it can be a way to communicate complex changes to data, and to separate UI from business logic. But before we go into how you can start using this pattern, let's go over some real world examples to make sure we understand what it is.
Let's say you find out about a new magazine that releases new issues every month. You want to keep up with all the new issues right when they come out. So you subscribe to the magazine and then every month when a new issue is released it shows up at your door. Great, right? Eventually you might get bored of the content, or you get sick of paying for it every month, so you unsubscribe from it. Then, the next time an issue is released it doesn't show up at your door.
This is a real world example of the observer pattern. Whether you've known it or not, you probably run into this every single day. Things like email newsletters, RSS feeds or subscribing to tweets from your friends are other examples.
The Observer Pattern
There are two components to the observer pattern. The subject and the observer. Each subject can have many observers. When an observer subscribers to the subject, it will be informed when changes happen to the subject. The diagram below gives a quick overview of the structure, but we'll dig deeper in just a second.
The Subject
Look at the subject as the thing that's going to be observed. Every subject needs a method to subscribe and a method to unsubscribe. Internally this just adds and removes a function from an array.
Once subscribing and unsubscribing from the subject has been setup, any time you want to inform subscribers of something, you just have to iterate over each if them and pass them the data they should know about.
So depending on what the subject is, you might provide a method for others to update some local state, and every time that local state is updated you let all the subscribers know by passing them the updated state.
Or maybe the subject periodically fetches new data from am API and any time the data changes it lets subscribers know.
The possibilities are endless as long as you include a way to subscribe and unsubscribe, and make calls to each subscriber whenever something important happens.
Here's an example of a super basic implementation of an observer subject.
class Subject {
subscribers = []
subscribe = (functionToSubscribe) => {
this.subscribers.push(functionToSubscribe)
}
unsubscribe = (functionToUnsubscribe) => {
this.subscribers = this.subscribers
.filter(func => func !== functionToUnsubscribe)
}
action = (data) => {
this.subscribers
.forEach(subscriber => subscriber(data))
}
}
The Observer
Once the subject is all setup and ready to accept subscribers, you just have to pass a function to the subscribe method to get started. This function you pass to subscribe is called an observer. You can have lots of observers watching a single subject. Observers can be added and removed whenever they want. An observer should expect some sort of data to be passed in related to the subject and should perform some sort of action because of it.
// Create a new subject to observe.
const subject = new Subject()
// Create a new function and subscribe to the subject.
const observerFunction = (data) => console.log(data)
subject.subscribe(observerFunction)
// Stuff happens with subject
subject.action('foo')
subject.action('bar')
subject.unsubscribe(observerFunction)
// More stuff happens, but the `observerFunction` will never find out.
subject.action('baz')
Examples
Now that we know all the components of the Observer pattern, let's take a look at a few examples.
Counter
In this example the subject is a counter. It provides methods to subscribe, unsubscribe, and add to the count. Any time you click the 1 or -1 buttons the count will change.
There are then three observers that store and display the last count they are informed of. Initially none are observing the count, but you can toggle this by pressing the subscribe/unsubscribe button.
Play around with it for a while, change subscriptions and change the count to see which ones update and which ones don't and when.
Toasts
While the counter is fun to play with and gives a good visual of how the observer pattern works, it's not super practical. Toasts, on the other hand, are a bit more practical and are a much more real-world example of when this pattern is useful.
If you've never heard the term "toast" before, it's a notification that pops up (you know, like toast out of a toaster) briefly when you're on a site. Usually it's used to provide feedback after the results come back from some previous action you performed.
So in our example, our subject is toasts, and our observer is the UI that will display new toasts as they are received. So the toasts subject has methods to subscribe and unsubscribe from new toasts, and it also has methods for creating success toasts, warning toasts, and error toasts.
Each time the success, warning, and error methods are called, the new toasts are sent to the subscribers. When our observer receives a new toast, it briefly renders it.
By implementing our toasts in this way, we can have one subject and one observer and call the success, warning, and error methods from anywhere in our codebase to display a toast. This is really useful in providing a generic tool the rest of our codebase can use to render a toast while keeping the UI logic out of the business logic that needs to trigger it.
That's It!
Pretty simple, right? Now you're an expert on the observer pattern. This article is part of an ongoing series on web development design patterns, so now go check out the others or come back later for more.
Top comments (2)
Nice!
This is a fundamental part of how Vue works 'under the hood,' BTW.
Great article! I didn't know that creating subscribers/observers were so easy!