Observer Design Pattern Python for Web Developers
In this post, you will learn what Observer design pattern is, how to implement it using Python, when it makes sense to use it, and when using it will be an overkill.
If you are Angular 2 developer or you playing around with libraries like RxJS, you will find this article double helpful, because Angular2 and RxJS are heavily relying on Observer design pattern.
If you are new to design patterns read What is Design Pattern first.
The Observer is a behavioral design pattern. The easiest way to think of it is as a publisher-subscribers kind of relationship between objects. Some of the objects may want to get notified when another object state will change and handle it in some way.
There are many many ways to implement the Observer design pattern. I propose what used to works best for myself, the implementation that is inspired by the RxJS library.
from abc import ABC class Observer(ABC): def handle_event(self, state): pass class Observable(ABC): def __init__(self, state): self._observers = set() self.state = state def subscribe(self, observer): self._observers.add(observer) def unsubscribe(self, observer): self._observers.discard(observer) def changeState(self, state): if (self.state != state): self.state = state for observer in self._observers: observer.handle_event(state)
Our implementation is based on two abstract classes -
Observer is a base class for any objects that want to respond to events generated by
Observable objects is a base class for purposes that other objects can subscribe to receive notifications about their status changes.
That implementation keeps our codebase clean because its easy to make any class observer or observable, without polluting their class definitions.
Let’s create an example of observer and observable classes to see how that implementation works.
class Guru(Observable): def __init__(self, name): super().__init__("I'm sitting") self.name = name class Follower(Observer): def __init__(self, name, guru): self.name = name guru.subscribe(self) def handle_event(self, state): print(self.name + " say: Hurra! My guru has new state '" + state + "'!")
As you see, both observer and observable implementation are very simple.
Guru is the class for objects that are meant to have followers, and the
Follower is the class for objects which may subscribe to any of the guru objects to receive notifications about their status change.
The follower may subscribe to any number of guru objects, or unsubscribe if he like it.
Let’s create instances of these classes and test it:
GuruBob = Guru("Crazy Bob") followerMichelle = Follower("Michelle", GuruBob) followerRoger = Follower("Roger", GuruBob) GuruBob.changeState("I'm lying down") GuruBob.unsubscribe(followerMichelle) GuruBob.changeState("I'm flying")
The output of this program is:
Roger say: Hurra! My guru has new state 'I'm lying down'! Michelle say: Hurra! My guru has new state 'I'm lying down'! Roger say: Hurra! My guru has new state 'I'm flying'!
So we have a guru object called Crazy Bob, who has two followers: Michelle and Roger. Both of the followers are following Crazy Bob, so when the Crazy Bob changed his state, his followers' event handlers were invoked, printing the messages to the screen.
Sadly, after the last state change, Michelle decided to unsubscribe from receiving state change notifications from guru Crazy Bob, so when the Crazy Bob changed his state to ‘I’m flying’ the Roger was the only one who responded.
What we can also do is add a conditional statement to
Observer.handle_event() method body. Because we have access to “state” we can filter the states that we are only interested in.
The observer pattern is a good choice when we have an object that has a status, and many other objects are interested in receiving notifications about its status change.
It doesn’t make sense to use this pattern in the situation when there is only a one-to-one relationship between two objects. In that case, there are other more suitable design patterns to model that kind of relationship.
Observer is another very simple but powerful design pattern at our disposal. It let us model complex relationships between objects while keeping the code base simple and clean.
Even if I don’t use this design pattern very often, just by knowing how it works, it makes me craft more mature code. That’s why I encourage you to nail this simple design pattern as well.
Observer is also a cornerstone of the popular reactive programming paradigm, and Angular 2 framework. So if you are working in these domains, an understanding Observer design pattern is a must.
If you have any questions related to this article, leave the comment below.
If you found this post helpful, consider sharing it, so I will be motivated to publish more posts that will help you get the IT career you want.
See you next time, and as always, stay focused!