Introducción
El patrón de diseño Observer es un patrón de comportamiento que define una relación de dependencia uno a muchos entre objetos, de manera que cuando un objeto cambia su estado, todos sus dependientes son notificados y actualizados automáticamente. Este patrón es especialmente útil en situaciones donde un cambio en un objeto debe reflejarse en otros objetos sin que estos estén estrechamente acoplados.
Conceptos Clave
- Sujeto (Subject): El objeto que mantiene una lista de dependientes (observadores) y notifica a estos cuando cambia su estado.
- Observador (Observer): El objeto que desea ser notificado cuando el sujeto cambia su estado.
- Notificación: El proceso mediante el cual el sujeto informa a los observadores sobre los cambios en su estado.
Estructura
La estructura del patrón Observer se puede desglosar en los siguientes componentes:
- Subject: Interfaz o clase abstracta que define los métodos para agregar, eliminar y notificar observadores.
- ConcreteSubject: Implementación concreta del Subject. Mantiene el estado del que los observadores dependen.
- Observer: Interfaz o clase abstracta que define el método
update
que será llamado cuando el Subject cambie. - ConcreteObserver: Implementación concreta del Observer. Actualiza su estado para mantenerse consistente con el Subject.
Diagrama UML
+----------------+ +-----------------+ | Subject | | Observer | +----------------+ +-----------------+ | +attach() | | +update() | | +detach() | +-----------------+ | +notify() | +----------------+ ^ | | +------------------+ | ConcreteSubject | +------------------+ | -state | | +getState() | | +setState() | +------------------+ ^ | | +------------------+ | ConcreteObserver | +------------------+ | -subject | | -observerState | | +update() | +------------------+
Ejemplo Práctico
Vamos a implementar un ejemplo práctico en Python para ilustrar cómo funciona el patrón Observer.
Código
Subject.py
class Subject: 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)
ConcreteSubject.py
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()
Observer.py
ConcreteObserver.py
class ConcreteObserver(Observer): def __init__(self, name): self._name = name def update(self, subject): print(f"{self._name} has been notified. New state is: {subject.state}")
Main.py
if __name__ == "__main__": subject = ConcreteSubject() observer1 = ConcreteObserver("Observer 1") observer2 = ConcreteObserver("Observer 2") subject.attach(observer1) subject.attach(observer2) subject.state = "State 1" subject.state = "State 2"
Explicación del Código
- Subject: Define los métodos
attach
,detach
ynotify
para gestionar la lista de observadores. - ConcreteSubject: Implementa el Subject y mantiene el estado. Cuando el estado cambia, se llama al método
notify
para actualizar a los observadores. - Observer: Define el método
update
que será llamado cuando el Subject cambie. - ConcreteObserver: Implementa el método
update
para realizar la acción deseada cuando se notifica un cambio en el Subject. - Main: Crea instancias de
ConcreteSubject
yConcreteObserver
, y demuestra cómo los observadores son notificados cuando cambia el estado del sujeto.
Ejercicio Práctico
Ejercicio
Implementa un sistema de notificaciones para una aplicación de noticias. Cuando se publica una nueva noticia, todos los suscriptores deben ser notificados.
Requisitos
- Crea una clase
NewsPublisher
que actúe como el Subject. - Crea una clase
Subscriber
que actúe como el Observer. - Los suscriptores deben ser notificados con el título de la nueva noticia.
Solución
NewsPublisher.py
class NewsPublisher: def __init__(self): self._subscribers = [] self._latest_news = None def attach(self, subscriber): self._subscribers.append(subscriber) def detach(self, subscriber): self._subscribers.remove(subscriber) def notify(self): for subscriber in self._subscribers: subscriber.update(self) def add_news(self, news): self._latest_news = news self.notify() @property def latest_news(self): return self._latest_news
Subscriber.py
class Subscriber: def __init__(self, name): self._name = name def update(self, publisher): print(f"{self._name} received news: {publisher.latest_news}")
Main.py
if __name__ == "__main__": publisher = NewsPublisher() subscriber1 = Subscriber("Subscriber 1") subscriber2 = Subscriber("Subscriber 2") publisher.attach(subscriber1) publisher.attach(subscriber2) publisher.add_news("Breaking News: Observer Pattern in Action!") publisher.add_news("Update: Observer Pattern Example Completed!")
Explicación de la Solución
- NewsPublisher: Actúa como el Subject, manteniendo una lista de suscriptores y notificándolos cuando se publica una nueva noticia.
- Subscriber: Actúa como el Observer, recibiendo notificaciones con el título de la nueva noticia.
- Main: Demuestra cómo los suscriptores son notificados cuando se publica una nueva noticia.
Conclusión
El patrón Observer es una herramienta poderosa para gestionar dependencias uno a muchos entre objetos, permitiendo que los cambios en un objeto se reflejen automáticamente en otros sin un acoplamiento estrecho. Este patrón es ampliamente utilizado en sistemas de notificación, interfaces de usuario y cualquier situación donde un cambio en un objeto debe ser comunicado a múltiples dependientes.
Al dominar el patrón Observer, estarás mejor preparado para diseñar sistemas flexibles y mantenibles que respondan eficientemente a cambios en el estado de los objetos.
Curso de Patrones de Diseño de Software
Módulo 1: Introducción a los Patrones de Diseño
- ¿Qué son los Patrones de Diseño?
- Historia y Origen de los Patrones de Diseño
- Clasificación de los Patrones de Diseño
- Ventajas y Desventajas de Usar Patrones de Diseño
Módulo 2: Patrones Creacionales
- Introducción a los Patrones Creacionales
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
Módulo 3: Patrones Estructurales
Módulo 4: Patrones de Comportamiento
- Introducción a los Patrones de Comportamiento
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Módulo 5: Aplicación de Patrones de Diseño
- Cómo Seleccionar el Patrón Adecuado
- Ejemplos Prácticos de Uso de Patrones
- Patrones de Diseño en Proyectos Reales
- Refactorización Usando Patrones de Diseño
Módulo 6: Patrones de Diseño Avanzados
- Patrones de Diseño en Arquitecturas Modernas
- Patrones de Diseño en Microservicios
- Patrones de Diseño en Sistemas Distribuidos
- Patrones de Diseño en Desarrollo Ágil