Observer design pattern in python

This design pattern falls under the category of behavioral design pattern. We know that the software consists of many objects, and sometimes all the objects are independent of each other. Sometimes they develop dependencies among themselves, meaning if there is a change in one object, it will affect all other objects and be modified accordingly. An observer design pattern deals with software where objects develop dependencies among themselves. If there is a change in any object's data, then we have to modify others accordingly. In an observer design pattern, the dependent objects are notified by some automatic process, and the user does not have to change objects individually. If one object's data is changed automatically, other objects will be modified, and then all of them will display data in no time.

Definition: It is a method in software design that is composed of a subject (an object which will be modified first )  and an observer. A list of observers exists, and the information about internal state change is transmitted everywhere quickly. If there appears a change in the subject, then all the observers will modify it automatically.

For the sake of simplicity, we call this method subscriber and publisher method, where one subscribes to a publisher. Then the publisher notifies the subscriber whenever new content is published . for example, think about youtube, in youtube We often see that most YouTubers say - " do subscribe and press the bell icon, so you will be getting a notification whenever I will try something, or you will never miss an update "as a person, what do you do when you like someone's content? You will subscribe and press the bell icon to get updates most frequently. This is a real-life example of the publisher and subscriber method. You are the subscriber, you are subscribing to a youtube channel, and whenever that particular YouTuber uploads any content, you are getting notified. Still, you think once there are many YouTubers, and as a youtube user, do you get notified when every YouTuber publishes something? The answer is complete no! You will get notified about those particular channels to which you have subscribed. This happens because you both have developed dependencies between you, so if anything is updating, your object is also involved. You are getting notified. one more thing to be noticed, as you have learned about the subscriber and publisher method, are you the only subscriber who is getting notified if anything is published? , so I hope all of you can answer this question. The answer is complete no ! all the subscribers who have subscribed to that particular channel will be getting a notification if anything is published. It's not only you, so as per the definition of observer design pattern ( it is a method in software design in which there exists a subject (an object which will be modified first )  and observer, if there appears a change in the subject then all the observers will modify automatically ) the youtube channel is working as subject and all of the subscribers are working as an observer and one thing to be noticed is a subject can develop dependencies with many observers. They will be modified/notified about the change in the subject's state.

Problem statement :

Suppose you are the owner of a sweet shop and your shop is famous, you have many stores across the state, and every day, many buyers visit your store. Your chef has made a new sweet of chocolate, so you thought that you would try to sell it. When you kept that sweet in your shop, everybody liked it. Now the condition is most of the buyer visits your store only for that particular sweet so most often many customers buy no sweets as there exists a shortage of that sweet. You instructed that more chefs will prepare that sweet to reduce the shortage, but it is still not working due to its popularity, and now you cannot make more chefs prepare that sweet. The customers are disappointed so what could be done?

So how will the problem be solved? The one solution is that you will send an email to every buyer of your store about the availability of the sweet and as they are aware of the availability so if the sweet is available, then they will visit, and otherwise, they will not. Still, there is a problem, not every buyer wants that particular sweet, so it will upset the buyers who are not interested in that sweet, and you will send tons of useless emails. So the problem is that you want to notify the buyers about the product's availability only to those buyers who are genuinely interested, but by using this method, either the customer wastes their time by checking availability or the shop sends useless emails.

Solution statement :

This problem can be easily solved using an observer design pattern. Our subject is a product, and the main factor of the subject is its state. Here state refers to the product's availability so that the subject will notify the publisher. The objects which will track the publisher are known as subscribers as they are interested in the information about the state change of the subject.

We will implement the subscription mechanism in the object so that whenever there is a change in the state, the subscriber will get notified, and all the subscriber who has subscribed to that particular channel will get a notification if anything is published. There would be a mechanism of unsubscribing, too, which will allow the user to unsubscribe from the content of that particular publisher. The unsubscribe feature is also important; otherwise, even if the user is no longer interested, but still, they will continuously receive the notification about the product, so it will waste time and disappoint the user, as well as the resources of the publisher, which will face wastage as it is sending messages to wrong customers who no longer wants to buy the product, so we have to implement subscribe and well as unsubscribe feature carefully, it will make the system efficient and the resources of the publisher will be used efficiently.

The overall system will consist of two particular data structures, the first is the array of subscribers, which will contain the information about all subscribers, and the next is various public methods which will allow us to add and remove the subscriber whenever anybody subscribes to the publisher, it will add them to an array using the method and if they unsubscribe they will be removed from the array list that's how the system remains efficient over the time. The communication interface remains the same though the subject is communicated to the publisher, and then the publisher communicates to the subscribers. The interface contains notification methods along with various methods, which are important. The publisher can also use some contextual data along with the notification.

A real-life example of an observer design pattern :

The most common example is email. When we visit some specific website, we often get a popup notification " would you like to receive emails" if you press yes, you will start getting emails. Sometimes when we share our details with the website or app, they start sending us mail. This happens because they add us to their mailing list. The method is simple whenever we receive mail. We start getting notified by this publisher, but if we no longer want to receive mail, we can unsubscribe. At the same time, check the bottom-most section. There, you will find the option to unsubscribe. If you click there, then you will successfully unsubscribe to that publisher. They will no longer send you that emails. This terminology is the same as an observer design pattern.

Normal implementation of observer design pattern :

exampe1.py

from abc import ABC, abstract method
class our_Subject:
    def __init__(self):
        self._observers = set()
        self._subject_state = None


    def _register(self, observer):
        observer._subject = self
        self._observers.add(observer)


    def _deregister(self, observer):
        observer._subject = None
        self._observers.remove(observer)


    def _notify(self):
        for observer in self._observers:
            observer.update(self._subject_state)


    @property
    def subject_state(self):
        return self._subject_state


    @subject_state.setter
    def subject_state(self, arg):
        self._subject_state = arg
        self._notify()
class Observer_here():
    def __init__(self):
        self._subject = None
        self._observer_state = None


  
    def update(self, arg):
        pass
class Concrete_Observer_here(Observer_here):
    def update(self, arg):
        self._observer_state = arg
        # ...
def main():
    the_subject = our_Subject()
    the_concrete_observer_here = Concrete_Observer_here()
    the_subject._register(the_concrete_observer_here)
    the_subject.subject_state = 123




if __name__ == "__main__":
    main()
print("This is observer method of python design patterns")
print("This is observer method of python design patterns")

Output:

This is observer method of python design patterns
This is observer method of python design patterns

Explanation :

This is the implementation of the observer design pattern's basic structure. This is the only blueprint through which we will get the idea of how and where we have to define and implement things while working with the software, which requires an observer design pattern. Looking at the code above, you will get all the functions, such as observer, subject, and publisher.

Subject class:

class our_Subject:
    def __init__(self):
        self._observers = set()
        self._subject_state = None

This is our subject class, and we have defined all the necessary attributes. The attributes change as needed and depend upon what we want from the design pattern. So this was about the basic structure of the observer design pattern. Other classes follow the same structure and work in the same manner. In the next example, we will try to solve some problems.

Example.py

class Observer_here:


    def __init__(self, observable):
        observable.subscribe(self)


    def notify(
        observable,
        self,
       *args_of,
        **kwargs_of
        ):
        print (' the message was received ', args_of, kwargs_of, 'From the sweet ', observable)
class Observable_here:
    def __init__(self):
        self._observers = []
    def subscribe(self, observer):
        self._observers.append(observer)
    def notify_observers_now(self, *args_of, **kwargs_of):
        for obs in self._observers:
            obs.notify(self, *args_of, **kwargs_of)
    def unsubscribe_from(self, observer):
        self._observers.remove(observer)
subject = Observable_here()
# Initializing all the observers 
observer1 = Observer_here(subject)
observer3 = Observer_here(subject)
observer2 = Observer_here(subject)
# The following message is for the notification feature 
subject.notify_observers_now('This is our 1st broadcast :) hope you liked it,
                         kw='From the Observer')
subject.unsubscribe_from(observer2)
# the unsubscribe feature is also used here 
subject.notify_observers_now('This is our 2nd broadcast :) hope you liked it,
                         kw='From the Observer')
subject.notify_observers_now('This is our 3rd broadcast :) hope you liked it,
                         kw='From the Observer')
subject.unsubscribe_from(observer3)

Output :

The message was received  ('This is our 1st broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bbd0>
 the message was received  ('This is our 1st broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bf50>
 the message was received  ('This is our 1st broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00b550>
 the message was received  ('This is our 2nd broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bbd0>
 the message was received  ('This is our 2nd broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bf50>
 the message was received  ('This is our 3rd broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bbd0>
 the message was received  ('This is our 3rd broadcast :) hope you liked it,) {'kw': 'From the Observer'} From the sweet  <__main__.Observer_here object at 0x7fac5d00bf50>

Explanation :

This is an example of the publisher and subscriber method. We have created some observers and publisher classes, which will let the observer know if any change is encountered in the subject.

And then, the notification will be sent to every subscriber, and we have also created an unsubscribe method, so if any user unsubscribed, they will not receive any notification.

Observer class:

class Observer_here:
    def __init__(self, observable):
        observable.subscribe(self)
    def notify(
        observable,
        self,
       *args_of,
        **kwargs_of
        ):

Here we have declared some pointers to execute the change feature. If any user unsubscribes, we have to make changes in the list of users, so the pointer will be helpful.

Notifying and unsubscribing:

subject.notify_observers_now('This is our 1st broadcast :) hope you liked it,
                         kw='From the Observer')
subject.unsubscribe_from(observer2)

First, we make the notification available to all the observers using notify function, and then we unsubscribe observer two using the unsubscribe function.

This is how the code works, and the observer design pattern is implemented.

Advantages of observer design pattern :

  • The open-close principle is maintained here.
  • It uses the resources efficiently.
  • Making relation between the objects are very simple; you can even make relation during runtime.
  • It is easy to implement and benefits both the subject and the observer.

Disadvantages of observer design pattern :

  • The random notification technique cannot be implemented in the observer design pattern as the user cannot be notified randomly. We have to notify everyone, or we will not notify anyone.