diff --git a/behavioral/observer/concrete_observers.py b/behavioral/observer/concrete_observers.py new file mode 100644 index 0000000..63c755e --- /dev/null +++ b/behavioral/observer/concrete_observers.py @@ -0,0 +1,18 @@ +""" +Concrete Observers react to the updates issued by the Subject they had been +attached to. +""" +from behavioral.observer.observer import Observer +from behavioral.observer.subject import Subject + + +class ConcreteObserverA(Observer): + def update(self, subject: Subject) -> None: + if subject.state < 3: + print("ConcreteObserverA: Reacted to the event") + + +class ConcreteObserverB(Observer): + def update(self, subject: Subject) -> None: + if subject.state == 0 or subject.state >= 2: + print("ConcreteObserverB: Reacted to the event") diff --git a/behavioral/observer/concrete_subject.py b/behavioral/observer/concrete_subject.py new file mode 100644 index 0000000..fb04d2c --- /dev/null +++ b/behavioral/observer/concrete_subject.py @@ -0,0 +1,61 @@ +from random import randrange +from typing import List, Optional + +from behavioral.observer.observer import Observer +from behavioral.observer.subject import Subject + + +class ConcreteSubject(Subject): + """ + The Subject owns some important state and notifies observers when the state + changes. + """ + + """ + For the sake of simplicity, the Subject's state, essential to all + subscribers, is stored in this variable. + """ + _state: Optional[int] = None + + """ + List of subscribers. In real life, the list of subscribers can be stored + more comprehensively (categorized by event type, etc.). + """ + _observers: List[Observer] = [] + + @property + def state(self) -> int: + return self._state + + def attach(self, observer: Observer) -> None: + print("Subject: Attached an observer.") + self._observers.append(observer) + + def detach(self, observer: Observer) -> None: + self._observers.remove(observer) + + """ + The subscription management methods. + """ + + def notify(self) -> None: + """ + Trigger an update in each subscriber. + """ + print("Subject: Notifying observers...") + for observer in self._observers: + observer.update(self) + + def some_business_logic(self) -> None: + """ + Usually, the subscription logic is only a fraction of what a Subject can + really do. Subjects commonly hold some important business logic, that + triggers a notification method whenever something important is about to + happen (or after it). + """ + + print("\nSubject: I'm doing something important.") + self._state = randrange(0, 10) + + print(f"Subject: My state has just changed to: {self._state}") + self.notify() diff --git a/behavioral/observer/main.py b/behavioral/observer/main.py new file mode 100644 index 0000000..34a0fa1 --- /dev/null +++ b/behavioral/observer/main.py @@ -0,0 +1,23 @@ +"""The client code""" + +from behavioral.observer.concrete_observers import ConcreteObserverA, ConcreteObserverB + + +from behavioral.observer.concrete_subject import ConcreteSubject + +subject = ConcreteSubject() + + +observer_a = ConcreteObserverA() +subject.attach(observer_a) + +observer_b = ConcreteObserverB() +subject.attach(observer_b) + + +subject.some_business_logic() +subject.some_business_logic() + +subject.detach(observer_a) + +subject.some_business_logic() diff --git a/behavioral/observer/observer.py b/behavioral/observer/observer.py new file mode 100644 index 0000000..f3abbeb --- /dev/null +++ b/behavioral/observer/observer.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod + +from structural.proxy.subject import Subject + + +class Observer(ABC): + """ + The Observer interface declares the update method, used by subjects. + """ + + @abstractmethod + def update(self, subject: Subject) -> None: + """ + Receive update from subject. + """ + pass diff --git a/behavioral/observer/subject.py b/behavioral/observer/subject.py new file mode 100644 index 0000000..ae1f6c8 --- /dev/null +++ b/behavioral/observer/subject.py @@ -0,0 +1,35 @@ +from abc import ABC, abstractmethod + +from behavioral.observer.observer import Observer + + +class Subject(ABC): + """ + The Subject interface declares a set of methods for managing subscribers. + """ + + @property + @abstractmethod + def state(self) -> int: + pass + + @abstractmethod + def attach(self, observer: Observer) -> None: + """ + Attach an observer to the subject. + """ + pass + + @abstractmethod + def detach(self, observer: Observer) -> None: + """ + Detach an observer to the subject. + """ + pass + + @abstractmethod + def notify(self) -> None: + """ + Notify all observers about an event. + """ + pass