Patrón Observer
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 92 de 110.
El patrón Observer establece una relación de uno a muchos entre objetos: cuando un objeto (el sujeto) cambia de estado, todos sus dependientes (observadores) son notificados automáticamente. Esto es perfecto para escenarios como actualizaciones de la interfaz de usuario (UI), sistemas de eventos o cualquier situación en la que múltiples partes de su aplicación necesiten reaccionar a los cambios.
El patrón involucra dos roles clave: un Subject que mantiene una lista de observadores y les notifica los cambios, y Observers que se registran a sí mismos y responden a las actualizaciones:
abstract class Observer {
void update(String message);
}
class NewsAgency {
final List<Observer> _observers = [];
String _latestNews = '';
void subscribe(Observer observer) {
_observers.add(observer);
}
void unsubscribe(Observer observer) {
_observers.remove(observer);
}
void publishNews(String news) {
_latestNews = news;
for (var observer in _observers) {
observer.update(_latestNews);
}
}
}
class NewsChannel implements Observer {
final String name;
NewsChannel(this.name);
@override
void update(String message) {
print('$name received: $message');
}
}
void main() {
var agency = NewsAgency();
var cnn = NewsChannel('CNN');
var bbc = NewsChannel('BBC');
agency.subscribe(cnn);
agency.subscribe(bbc);
agency.publishNews('Breaking news!');
// CNN received: Breaking news!
// BBC received: Breaking news!
}Cuando se llama a publishNews, la agencia itera a través de todos los observadores suscritos y llama a su método update. Los observadores pueden suscribirse o cancelar su suscripción en cualquier momento, lo que hace que el sistema sea flexible y esté débilmente acoplado: el sujeto no necesita conocer los tipos específicos de sus observadores.
Desafío
Fácil¡Vamos a construir un sistema de alerta de precios de acciones utilizando el patrón Observer! Crearás un sistema donde un marcador de acciones (stock ticker) notifica a múltiples inversores cada vez que el precio cambia, permitiendo que cada inversor reaccione a las actualizaciones del mercado a su manera.
Organizarás tu código en dos archivos:
stock_system.dart: Crea tu sistema de observadores aquí:- Una clase abstracta
Investorcon un métodovoid onPriceChange(String stockName, double price) - Una clase
DayTraderque implementaInvestor. Tiene un camponamey su métodoonPriceChangeimprime:[name] sees [stockName] at $[price] - Making quick trade! - Una clase
LongTermInvestorque implementaInvestor. Tiene un camponamey su métodoonPriceChangeimprime:[name] notes [stockName] at $[price] - Holding steady. - Una clase
StockTicker(el sujeto) con:- Un campo
String stockName - Una lista privada para almacenar a los inversores suscritos
- Un método
subscribe(Investor investor)para añadir un inversor - Un método
unsubscribe(Investor investor)para eliminar un inversor - Un método
updatePrice(double newPrice)que notifica a todos los inversores suscritos sobre el nuevo precio
- Un campo
- Una clase abstracta
main.dart: Importa tu sistema de acciones y demuestra el patrón Observer en acción:- Crea un
StockTickerpara una acción llamadaDART - Crea un
DayTraderllamadoAlicey unLongTermInvestorllamadoBob - Suscribe a ambos inversores al ticker
- Actualiza el precio a
150.50(ambos inversores deben ser notificados) - Imprime una línea vacía
- Desvincula (unsubscribe) a Alice del ticker
- Actualiza el precio a
155.75(ahora solo Bob debería ser notificado)
- Crea un
Esto demuestra cómo el patrón Observer crea un sistema débilmente acoplado: el StockTicker no necesita saber si está notificando a operadores diarios (day traders) o a inversores a largo plazo, ¡simplemente llama a onPriceChange en quien esté suscrito!
Salida esperada:
Alice sees DART at $150.5 - Making quick trade!
Bob notes DART at $150.5 - Holding steady.
Bob notes DART at $155.75 - Holding steady.Hoja de referencia
El patrón Observer establece una relación de uno a muchos donde un sujeto notifica a todos sus observadores cuando su estado cambia.
El patrón tiene dos roles clave:
- Subject: Mantiene una lista de observadores y les notifica los cambios
- Observer: Se registra con el sujeto y responde a las actualizaciones
Implementación básica:
abstract class Observer {
void update(String message);
}
class Subject {
final List<Observer> _observers = [];
void subscribe(Observer observer) {
_observers.add(observer);
}
void unsubscribe(Observer observer) {
_observers.remove(observer);
}
void notifyObservers(String data) {
for (var observer in _observers) {
observer.update(data);
}
}
}
class ConcreteObserver implements Observer {
final String name;
ConcreteObserver(this.name);
@override
void update(String message) {
print('$name received: $message');
}
}Beneficios clave:
- Acoplamiento débil entre el sujeto y los observadores
- Los observadores pueden suscribirse/desuscribirse dinámicamente
- El sujeto no necesita conocer los tipos específicos de observadores
Pruébalo tú mismo
import 'stock_system.dart';
void main() {
// TODO: Crear un StockTicker para una acción llamada 'DART'
// TODO: Crear un DayTrader llamado 'Alice'
// TODO: Crear un LongTermInvestor llamado 'Bob'
// TODO: Suscribir a ambos inversores al ticker
// TODO: Actualizar el precio a 150.50 (ambos inversores deben ser notificados)
// TODO: Imprimir una línea vacía
// TODO: Cancelar la suscripción de Alice del ticker
// TODO: Actualizar el precio a 155.75 (ahora solo Bob debe ser notificado)
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository