Learn the design pattern "Observer" in Python

As a material for learning GoF design patterns, the book "Introduction to Design Patterns Learned in the Augmented and Revised Java Language" seems to be helpful. However, since the examples taken up are based on JAVA, I tried the same practice in Python to deepen my understanding.

■ Observer pattern (observer pattern)

The Observer pattern is a type of design pattern used in the process of notifying other objects of an event (event) of an object in the program. This is called because the object side to be notified is observed by the object side to be notified (English: observe). Also known as the publish-subscribe model. It is closely related to the principle of implicit calling. It is also used to implement a distributed event processing system. Depending on the language, the problems dealt with in this pattern are handled by the language's event handling syntax.

UML class and sequence diagram W3sDesign_Observer_Design_Pattern_UML.jpg UML class diagram 2880px-Observer_w_update.svg.png

(The above is quoted from Wikipedia)

□ Memorandum

In the ʻObserver pattern, when the state of the observation target changes, the observer is notified, so it seems to be effective when describing the processing according to the state change. The original meaning of the word ʻobserver is" observer ", but in reality, the role of ʻObserver is passive rather than actively" observing "but being" notified "by the role of Subject. It is said that it is sometimes called the Publish-Subscribepattern because it will be waiting for you. Certainly, I felt that the expressionspublish and subscribe` were more appropriate.

■ "Observer" sample program

Actually, I would like to run a sample program that utilizes the Observer pattern and check the following behavior.

――The observer observes the object that generates a lot of numbers and displays the value. ――The display method differs depending on the observer. --DigitalObserver displays the value numerically --GraphicObserver displays the values in a simple graph

$ python Main.py 
DigitObservser: 30
GraphicObserver:******************************
DigitObservser: 48
GraphicObserver:************************************************
DigitObservser: 6
GraphicObserver:******
DigitObservser: 19
GraphicObserver:*******************
DigitObservser: 19
GraphicObserver:*******************
DigitObservser: 45
GraphicObserver:*********************************************
DigitObservser: 8
GraphicObserver:********
DigitObservser: 21
GraphicObserver:*********************
DigitObservser: 40
GraphicObserver:****************************************
DigitObservser: 6
GraphicObserver:******
DigitObservser: 1
GraphicObserver:*
DigitObservser: 9
GraphicObserver:*********
DigitObservser: 26
GraphicObserver:**************************
DigitObservser: 22
GraphicObserver:**********************
DigitObservser: 16
GraphicObserver:****************
DigitObservser: 10
GraphicObserver:**********
DigitObservser: 45
GraphicObserver:*********************************************
DigitObservser: 1
GraphicObserver:*
DigitObservser: 36
GraphicObserver:************************************
DigitObservser: 45
GraphicObserver:*********************************************

■ Details of sample program

Similar code has been uploaded to the Git repository. https://github.com/ttsubo/study_of_design_pattern/tree/master/Observer

--Directory structure

.
├── Main.py
└── observer
    ├── __init__.py
    ├── generator.py
    └── observer.py

(1) The role of Subject

The Subject role represents the" observed side ". The Subject role has a method for registering the observer ʻObserverrole and a method for deleting it. It also declares a "get current state" method. In the sample program, theNumberGenerator` class serves this role.

observer/generator.py


import random
from abc import ABCMeta, abstractmethod

class NumberGenerator(metaclass=ABCMeta):
    def __init__(self):
        self.__observers = []

    def addObserver(self, observer):
        self.__observers.append(observer)

    def deleteObserver(self, observer):
        self.__observers.remove(observer)

    def notifyObserver(self):
        for o in self.__observers:
            o.update(self)

    @abstractmethod
    def getNumber(self):
        pass

    @abstractmethod
    def execute(self):
        pass

(2) The role of Concrete Subject

The Concrete Subject role is a role that expresses a concrete" observed side ". When the state changes, tell the registered ʻObserverrole. In the sample program, theRandomNumberGenerator` class serves this role.

observer/generator.py


class RandomNumberGenerator(NumberGenerator):
    def __init__(self):
        self.__number = 0
        super(RandomNumberGenerator, self).__init__()

    def getNumber(self):
        return self.__number

    def execute(self):
        for _ in range(20):
            self.__number = random.randint(0, 49)
            self.notifyObserver()

(3) The role of Observer (observer)

The role of ʻObserver is the role of being told by the role of Subject that "the state has changed". The method for that is ʻupdate. In the sample program, the ʻObserver` class serves this role.

observer/observer.py


import time
from abc import ABCMeta, abstractmethod

class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, ganerator):
        pass

(4) The role of ConcreteObserver (concrete observer)

The ConcreteObserver role is the concrete ʻObserver. When the ʻupdate method is called, it gets the current state of the Subject role in that method. In the sample program, the DigitObserver and GraphObserver classes serve this role.

observer/observer.py


class DigitObserver(Observer):
    def update(self, generator):
        print("DigitObservser: {0}".format(generator.getNumber()))
        time.sleep(0.1)


class GraphObserver(Observer):
    def update(self, generator):
        print("GraphicObserver:", end='')
        count = generator.getNumber()
        for _ in range(count):
            print('*', end='')
        print("")
        time.sleep(0.1)

(5) The role of Client

In the sample program, the startMain method serves this role.

Main.py


from observer.observer import  DigitObserver, GraphObserver
from observer.generator import RandomNumberGenerator

def startMain():
    generator = RandomNumberGenerator()
    observer1 = DigitObserver()
    observer2 = GraphObserver()
    generator.addObserver(observer1)
    generator.addObserver(observer2)
    generator.execute()

if __name__ == '__main__':
    startMain()

■ Reference URL

-[Finishing "Introduction to Design Patterns Learned in Java Language" (Not)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java Introduction to Design Patterns Learned in Language-Finishing-Not-2cc9b34a30b2)

Recommended Posts

Learn the design pattern "Observer" in Python
Learn the design pattern "Prototype" in Python
Learn the design pattern "Flyweight" in Python
Learn the design pattern "Memento" in Python
Learn the design pattern "Proxy" in Python
Learn the design pattern "Command" in Python
Learn the design pattern "Visitor" in Python
Learn the design pattern "Bridge" in Python
Learn the design pattern "Mediator" in Python
Learn the design pattern "Decorator" in Python
Learn the design pattern "Iterator" in Python
Learn the design pattern "Strategy" in Python
Learn the design pattern "Composite" in Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Template Method" in Python
Learn the design pattern "Factory Method" in Python
Learn the design pattern "Chain of Responsibility" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "Facade" with Python
Implement the Singleton pattern in Python
Design Pattern #Observer
Singleton pattern in Python
Observer pattern in Java
Visitor pattern in Python
Download the file in Python
Find the difference in Python
Learn cumulative sum in Python
Design Patterns in Python: Introduction
Learn exploration in Python # 1 Full exploration
Python Design Pattern --Template method
Getting the arXiv API in Python
Python in the browser: Brython's recommendation
Save the binary file in Python
Hit the Sesami API in Python
Get the desktop path in Python
Get the script path in Python
In the python command python points to python3.8
Hit the web API in Python
I wrote the queue in Python
Calculate the previous month in Python
Examine the object's class in python
Get the desktop path in Python
Get the host name in Python
Access the Twitter API in Python
The first step in Python Matplotlib
I wrote the stack in Python
Master the weakref module in Python
Learn the basics of Python ① Beginners
Load the remote Python SDK in IntelliJ
Try using the Wunderlist API in Python
[Python Kivy] About changing the design theme
Try using the Kraken API in Python
Learn the basics while touching python Variables
Write the test in a python docstring
OR the List in Python (zip function)
GoF design pattern from the problem 2. Structure
Display Python 3 in the browser with MAMP
Tweet using the Twitter API in Python
Check if the URL exists in Python