[PYTHON] Design Pattern #Observer

I practiced design patterns so that I could write code that was conscious of design. Other Design Patterns will be released frequently.

Preface

The primary goal is to understand when, what, and how to use design patterns. (I'm new to Java or a statically typed language, and I don't have a long history of python, so I think there are some things that aren't like Pythonista. If you have any suggestions, please teach me.)

This time, the behavioral pattern Observer.

What is Observer

In the Observer pattern, the observer is notified when the state of the observation target changes. The Observer pattern is useful when describing processing that responds to state changes.

Overview

The sample program created here is for the observer to observe an object that generates a large number and display its value. However, the display method differs depending on the observer. DigitObserver displays the values numerically, while GraphObserver displays the values in a simple graph.

Overall class diagram

observer.py


from abc import ABCMeta, abstractmethod


class Observer(Exception):
    __meta__ = ABCMeta

    @abstractmethod
    def update(self, generator):
        pass

The observer interface is an interface that expresses an "observer". Specific observers implement this interface.

It is the NumberGenerator that generates the number that calls the update method. The update method is a method for NumberGenerator to tell the observer that "My content has been updated. Please update the display as well."

number_generator.py


from abc import ABCMeta, abstractmethod


class NumberGenerator(metaclass=ABCMeta):

    __observers = []

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

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

    def notify_observers(self):
        for observer in self.__observers:
            observer.update(self)

    @abstractmethod
    def get_number():
        pass

    @abstractmethod
    def execute():
        pass

The NumberGenerator class is an abstract class that generates numbers. The actual number generation (execute method) and the part to get the number (get_number method) are abstract methods, expecting the subclass to implement them.

The observers field is the field that stores the observers who are observing the Number Generator.

add_observer is a method to add an observer, and delete_observer is a method to delete an observer.

The notify_observers method tells all observers, "My content has been updated, please update your display." In this method, each observer in the observers calls the update method.

random_number_generator.py


import random
from number_generator import NumberGenerator


class RandomNumberGenerator(NumberGenerator):

    __number = 0

    def __init__(self):
        self.__rand = random

    def get_number(self):
        return self.__number

    def execute(self):
        for i in range(0, 20):
            self.__number = self.__rand.randint(0, 50)
            self.notify_observers()

The RandomNumberGenerator class is a subclass of NumberGenerator that generates random numbers.

The random field holds the random number generator and the number field holds the current random number value.

The get_number method returns the value in the number field.

The execute method generates 20 random numbers (integers from 0 to 49) and uses notify_observers each time to notify the observer.

digit_observer.py


import time
import logging
from observer import Observer


class DigitObserver(Observer):

    def update(self, generator):
        print('DigitObserver:' + str(generator.get_number()))
        try:
            time.sleep(1)
        except InterruptedError as e:
            logging.exception(e)

The DigitObserver class is a class that implements the observer interface and is used to display the number of observations as a "number". Use the Get_number method of NumberGenerator given as an argument in the update method to get the number and display it. There are intervals so that you can see what the display looks like.

graph_observer.py


import sys
import time
from observer import Observer


class GraphObserver(Observer):

    def update(self, generator):
        sys.stdout.write('GraphObserver:')
        count = generator.get_number()
        for i in range(0, count):
            sys.stdout.write('*')
        print('')
        try:
            time.sleep(1)
        except InterruptedError:
            pass

The GraphObserver class is also a class that implements the Observer interface. This class represents the number of observations as a "simple graph" like *****.

main.py


from digit_observer import DigitObserver
from graph_observer import GraphObserver
from random_number_generator import RandomNumberGenerator


def main():
    generator = RandomNumberGenerator()
    observer1 = DigitObserver()
    observer2 = GraphObserver()
    generator.add_observer(observer1)
    generator.add_observer(observer2)
    generator.execute()


if __name__ == "__main__":
    main()

Create one instance of RandomNumberGenerator and two observers of it. observer1 is an instance of DigitObserver and observer2 is an instance of GraphObserver.

After registering the observer using the add_observer method, use generator.execute to generate the number.

Execution result

DigitObserver:17
GraphObserver:*****************
DigitObserver:43
GraphObserver:*******************************************
DigitObserver:47
GraphObserver:***********************************************
DigitObserver:34
GraphObserver:**********************************
DigitObserver:30
GraphObserver:******************************
DigitObserver:50
GraphObserver:**************************************************
DigitObserver:7
GraphObserver:*******
DigitObserver:40
GraphObserver:****************************************
DigitObserver:39
GraphObserver:***************************************
DigitObserver:41
GraphObserver:*****************************************
DigitObserver:38
GraphObserver:**************************************
DigitObserver:3
GraphObserver:***
DigitObserver:22
GraphObserver:**********************
DigitObserver:26
GraphObserver:**************************
DigitObserver:0
GraphObserver:
DigitObserver:23
GraphObserver:***********************

Summary

One object has been monitored by adding a method that registers the other object (observer). The monitored object sends a message to the registered observer when it changes.

It doesn't matter what class the observer handles that information for the monitored object, it can be any class of object.

Objects do not necessarily understand the reason, and as a result, the dependency between objects can be reduced, and the observer manages the notification destination so that the observation target does not need to be aware of the notification side.

reference

Recommended Posts

Design Pattern #Observer
Design Pattern #Builder
Design Pattern #Adapter
Design Pattern #Decorator
Design Pattern #Facade
Design Pattern #Strategy
Design Pattern #Singleton
Design Pattern #Proxy
Learn the design pattern "Observer" in Python
[Gang of Four] Design pattern learning --Observer
Observer pattern in Java
Design Pattern #Factory Method
Design Pattern #Template Method
Ore Ore Design Pattern: Glocal Variable
Python Design Pattern --Template method
Observer pattern understood by cats Part 1
Observer pattern understood by Nyanko Part 2
GoF java design pattern rough summary
Learn the design pattern "Prototype" in Python
Learn the design pattern "Builder" in Python
[Gang of Four] Design pattern learning --Singleton
[Gang of Four] Design Pattern Learning --Decorator
[Gang of Four] Design pattern learning --Visitor
Design pattern-Adapter
[Gang of Four] Design pattern learning --Mediator
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
[Gang of Four] Design pattern learning --Iterator
GoF design pattern from the problem 2. Structure
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
Design pattern-Iterator
[Gang of Four] Design pattern learning --Facade
[Gang of Four] Design pattern learning --Composite
[Gang of Four] Design pattern learning --Prototype
GoF design pattern from the problem 1. Generation
Learn the design pattern "Iterator" in Python
[Gang of Four] Design pattern learning --Memento
[Gang of Four] Design pattern learning --State
[Gang of Four] Design pattern learning --Interpreter
[Gang of Four] Design pattern learning --Builder
Learn the design pattern "Strategy" in Python
[Gang of Four] Design pattern learning --Bridge
Learn the design pattern "Composite" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
[Gang of Four] Design pattern learning --Proxy
[Gang of Four] Design pattern learning --Strategy
[Gang of Four] Design pattern learning --Adapter
Learn the design pattern "Facade" with Python
[Gang of Four] Design pattern learning --Command
GoF design pattern from the problem 3. Behavior
I studied about design patterns (personal memo) Part 7 (Observer pattern, Memento pattern, State pattern, Flyweight pattern)
[Gang of Four] Design pattern learning --Fly Weight
[Gang of Four] Design pattern learning --Abstract Factory
Learn the design pattern "Template Method" in Python