In this article, we will learn about Observer Design Pattern explained with a Python sample.
What is an Observer Design Pattern?
The Observer Design Pattern is a Behavioral Pattern used to notify all objects that are registered/attached/added to the same type of observer.
Why use the Observer Design Pattern?
It is useful when there is a requirement of "Single object change in its state/behavior/Value needs to notify all other objects which are observing the same object".
Basically, a single object should notify multiple objects of the same observer type. It's the relation of one object to many others.
Players in this pattern,
- Subject: Provides a contract to add/remove observers
- ConcreteSubject: Implements a contract defined by Subject
- Observer: Provides a contract for updating objects when there is a change in the subject state/behavior/Value
- ConcreteObserver: Implements a contract defined by Observer
Problem definition
Design a software solution for a wholesale pen seller. This wholesale pen seller will sell pens to shops. When there is a change in pen price per market demand, it should automatically notify the change in pen price to all shops.
The players are
- Subject – PenSubject
- ConcreteSubject – Pen
- Observer – ShopObserver
- ConcreteObserver – Shop
Below is the PenSubject abstract class which will define contact for ConcreteSubjects, add the below code in subject.py file
- from abc import ABC, abstractmethod
-
- class PenSubject(ABC):
-
- @abstractmethod
- def add(self, shop):
- pass
-
- @abstractmethod
- def remove(self, shop):
- pass
-
- @abstractmethod
- def notify(self):
- pass
Below is the Pen class which implements PenSubject abstract class to add/remove observers and call Notify, which will invoke when there is a change in pen price, add the below code in ConcreteSubject.py file
- from subject import PenSubject
-
-
- class Pen(PenSubject):
-
- def __init__(self, prize):
- self._penPrize = prize
-
- shops = []
-
- def add(self, shop):
- self.shops.append(shop)
-
- def remove(self, shop):
- self.shops.append(shop)
-
- def notify(self):
- for shop in self.shops:
- shop.update(self)
- print('---------------------------------------')
-
- @property
- def penPrize(self):
- return self._penPrize
-
- @penPrize.setter
- def penPrize(self, prize):
- self._penPrize = prize
- self.notify()
Below is the ShopObserver (Observer) abstract class which defines a contract for ConcreteObserver (i.e. in our case Shop), add the below code in observer.py file
- from abc import ABC, abstractmethod
-
-
- class ShopObserver(ABC):
-
- @abstractmethod
- def update(self, pen):
- pass
Below is the Shop class which implements ShopObserver to update the Subject object, add the below code in ConcreteObserver.py file
- from observer import ShopObserver
- from ConcreteSubject import Pen
-
-
- class Shop(ShopObserver):
-
- def __init__(self, shopName: str):
- self._shopName = shopName
-
- def update(self, pen: Pen):
- print("pen prize changed to ", pen.penPrize, ' in ', self._shopName)
We will see the execution and result now. Add code in startup.py file and run
- from ConcreteObserver import Shop
- from ConcreteSubject import Pen
-
- pen = Pen(10)
- pen.add(Shop('Shop1'))
- pen.add(Shop('Shop2'))
- pen.add(Shop('Shop3'))
-
- pen.penPrize = 15
- pen.penPrize = 20
- pen.penPrize = 32
Below is the result screenshot. In it, we can see that change in pen price value has notified all shops
Summary
In this article, we learned through a code example what the Observer design pattern is, as well as why and where to use it.