Introducció al Patró Observer

El patró Observer és un dels patrons de disseny de comportament més utilitzats en el desenvolupament de programari. Aquest patró defineix una relació de dependència un-a-molts entre objectes, de manera que quan un objecte canvia el seu estat, tots els seus dependents són notificats i actualitzats automàticament.

Objectius del Patró Observer

  • Desacoblar el subjecte dels seus observadors: Permet que els objectes es comuniquin sense conèixer les identitats dels altres.
  • Facilitar la reutilització del codi: Els observadors poden ser reutilitzats en diferents contextos sense modificar el subjecte.
  • Promoure la flexibilitat i l'escalabilitat: Afegir nous observadors no requereix canvis en el subjecte.

Components del Patró Observer

El patró Observer consta de quatre components principals:

  1. Subject (Subjecte): Manté una llista d'observadors i proporciona mètodes per afegir, eliminar i notificar observadors.
  2. Observer (Observador): Defineix una interfície per actualitzar-se en resposta a canvis en el subjecte.
  3. ConcreteSubject (Subjecte Concret): Implementa el subjecte i manté l'estat d'interès per als observadors.
  4. ConcreteObserver (Observador Concret): Implementa l'actualització per mantenir la seva consistència amb el subjecte.

Diagrama UML del Patró Observer

+-----------------+       +-----------------+
|     Subject     |<----->|     Observer    |
+-----------------+       +-----------------+
| + attach()      |       | + update()      |
| + detach()      |       +-----------------+
| + notify()      |
+-----------------+
        ^
        |
        |
+---------------------+
|  ConcreteSubject    |
+---------------------+
| - state             |
| + getState()        |
| + setState()        |
+---------------------+
        ^
        |
        |
+---------------------+
|  ConcreteObserver   |
+---------------------+
| - subject           |
| - state             |
| + update()          |
+---------------------+

Implementació del Patró Observer

A continuació es mostra una implementació del patró Observer en Python:

Subjecte i Observador

from abc import ABC, abstractmethod

class Subject(ABC):
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self):
        for observer in self._observers:
            observer.update(self)

class Observer(ABC):
    @abstractmethod
    def update(self, subject):
        pass

Subjecte Concret

class ConcreteSubject(Subject):
    def __init__(self):
        super().__init__()
        self._state = None

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value
        self.notify()

Observador Concret

class ConcreteObserver(Observer):
    def __init__(self, name):
        self._name = name
        self._state = None

    def update(self, subject):
        self._state = subject.state
        print(f'{self._name} ha estat notificat. Nou estat: {self._state}')

Exemple d'Ús

if __name__ == "__main__":
    subject = ConcreteSubject()

    observer1 = ConcreteObserver("Observador 1")
    observer2 = ConcreteObserver("Observador 2")

    subject.attach(observer1)
    subject.attach(observer2)

    subject.state = "Estat 1"
    subject.state = "Estat 2"

    subject.detach(observer1)
    subject.state = "Estat 3"

Exercicis Pràctics

Exercici 1: Implementar un Sistema de Notificació de Temperatura

Descripció: Implementa un sistema de notificació de temperatura on diversos dispositius (observadors) reben actualitzacions quan la temperatura (subjecte) canvia.

Requisits:

  • Crea una classe TemperatureSensor que actuï com a subjecte.
  • Crea classes DisplayDevice i LoggingDevice que actuïn com a observadors.
  • Quan la temperatura canviï, els dispositius han de ser notificats i han de mostrar o registrar la nova temperatura.

Solució Proposada

class TemperatureSensor(Subject):
    def __init__(self):
        super().__init__()
        self._temperature = None

    @property
    def temperature(self):
        return self._temperature

    @temperature.setter
    def temperature(self, value):
        self._temperature = value
        self.notify()

class DisplayDevice(Observer):
    def update(self, subject):
        print(f'Display: La nova temperatura és {subject.temperature}°C')

class LoggingDevice(Observer):
    def update(self, subject):
        print(f'Log: Temperatura registrada: {subject.temperature}°C')

if __name__ == "__main__":
    sensor = TemperatureSensor()

    display = DisplayDevice()
    logger = LoggingDevice()

    sensor.attach(display)
    sensor.attach(logger)

    sensor.temperature = 25
    sensor.temperature = 30

Errors Comuns i Consells

Errors Comuns

  • No notificar els observadors després d'un canvi d'estat: Assegura't de cridar el mètode notify() després de canviar l'estat del subjecte.
  • No desacoblar correctament el subjecte dels observadors: Utilitza interfícies o classes abstractes per definir els observadors i subjectes, permetent així una major flexibilitat i reutilització.

Consells

  • Utilitza patrons de disseny complementaris: El patró Observer es pot combinar amb altres patrons com el Singleton per assegurar que només hi hagi una instància del subjecte.
  • Gestiona les dependències: Assegura't que els observadors es registrin i es desregistrin correctament per evitar referències pendents que podrien causar fuites de memòria.

Resum

El patró Observer és una eina poderosa per gestionar la comunicació entre objectes en un sistema desacoblat. Permet que els objectes es notifiquin automàticament dels canvis d'estat, facilitant la mantenibilitat i l'escalabilitat del codi. Amb una comprensió clara dels seus components i una implementació adequada, aquest patró pot millorar significativament la qualitat del teu programari.

© Copyright 2024. Tots els drets reservats