Passed
Pull Request — dev (#32)
by Konstantinos
22:13 queued 01:54
created

so_magic.utils.notification.Observer.update()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
"""Typical subject/observers pattern implementation. You can see this pattern
2
mentioned also as event/notification or broadcast/listeners.
3
4
Provides the Observer class, serving as the interface that needs to be implemented by concrete classes; the update
5
method needs to be overrode. Concrete Observers react to the notifications/updates issued by the Subject they had been
6
attached to.
7
8
Provides the Subject class, serving with mechanisms to subscribe/unsubscribe (attach/detach) observers and also with a
9
method to "notify" all subscribers about events.
10
"""
11
12
from abc import ABC, abstractmethod
13
from typing import List
14
15
__all__ = ['Subject', 'Observer']
16
17
18
class ObserverInterface(ABC):
19
    """The Observer interface declares the update method, used by subjects.
20
21
    Enables objects to act as "event" listeners; react to "notifications"
22
    by executing specific handling logic.
23
    """
24
    @abstractmethod
25
    def update(self, *args, **kwargs) -> None:
26
        """Receive an update (from a subject); handle an event notification."""
27
        raise NotImplementedError
28
29
30
class SubjectInterface(ABC):
31
    """The Subject interface declares a set of methods for managing subscribers.
32
33
    Enables objects to act as "subjects of observations"; notify the subscribed observers/listeners.
34
    """
35
36
    @abstractmethod
37
    def attach(self, observer: ObserverInterface) -> None:
38
        """Attach an observer to the subject; subscribe the observer."""
39
        raise NotImplementedError
40
41
    @abstractmethod
42
    def detach(self, observer: ObserverInterface) -> None:
43
        """Detach an observer from the subject; unsubscribe the observer."""
44
        raise NotImplementedError
45
46
    @abstractmethod
47
    def notify(self) -> None:
48
        """Notify all observers about an event."""
49
        raise NotImplementedError
50
51
52
class Observer(ObserverInterface, ABC):
53
    pass
54
55
56
class Subject(SubjectInterface):
57
    """The Subject owns some important state and can notify observers.
58
59
    Both the _state and _observers attributes have a simple implementation,
60
    but can be overrode to accommodate for more complex scenarios.
61
62
    The observers/subscribers are implemented as a python list.
63
    In more complex scenarios, the list of subscribers can
64
    be stored more comprehensively (categorized by event type, etc.).
65
66
67
    The subscription management methods provided are 'attach' and 'detach'
68
    to add or remove a subscriber respectively
69
    """
70
    def __init__(self, *args, **kwargs):
71
        self._observers: List[Observer] = []
72
        self._state = None
73
74
    def add(self, *observers):
75
        """Subscribe multiple observers at once."""
76
        self._observers.extend(list(observers))
77
78
    @property
79
    def state(self):
80
        return self._state
81
82
    @state.setter
83
    def state(self, state):
84
        self._state = state
85
86
    def attach(self, observer: Observer) -> None:
87
        self._observers.append(observer)
88
89
    def detach(self, observer: Observer) -> None:
90
        self._observers.remove(observer)
91
92
    def notify(self) -> None:
93
        """Trigger an update in each subscriber/observer."""
94
        for observer in self._observers:
95
            observer.update(self)
96