← Volver al Blog

Patrón Observer en C#: Guía Completa con Ejemplos [2025]

⏱️ 9 min de lectura

Introducción: El Patrón Invisible que Usas Todos los Días

Hay patrones en la naturaleza y en el software que, una vez que los ves, no puedes dejar de verlos. El Patrón Observer es uno de ellos. Define una relación simple pero poderosa: algunas cosas cambian, y otras simplemente reaccionan.

Si alguna vez te has preguntado cómo te llega una notificación de YouTube al instante, cómo se actualiza un marcador deportivo en vivo, o cómo una hoja de cálculo recalcula todo cuando cambias una sola celda, estás en el lugar correcto.

Este patrón de comportamiento es la base de la programación reactiva y los sistemas en tiempo real. En esta guía completa, lo desglosaremos desde cero:

  • Empezaremos con una analogía en la naturaleza.
  • Implementaremos un ejemplo completo en C# paso a paso.
  • Veremos cómo llevarlo a aplicaciones reales en red con SignalR.
  • Y, lo más importante, cuándo NO deberías usarlo.
Amanecer en un bosque, simbolizando un cambio de estado y el Patrón Observer.

1. La Reacción Natural: El Sol y el Bosque

Imagina un bosque en mitad de la noche. Silencio absoluto. Pero entonces… algo cambia. El Sol empieza a salir.

El Sol como Sujeto

En terminología de diseño, el Sol es el Sujeto (o Subject). Es el objeto que es observado. Su único trabajo es cambiar su estado y, de alguna manera, "publicar" ese cambio.

Los Observadores del Bosque

Los Observadores (Observers) son los que reaccionan al cambio del Sujeto:

  • El Gallo recibe la señal (la luz) y empieza a cantar.
  • El Girasol recibe esa misma señal y se abre, girando para seguir al sol.
  • El Búho también recibe la señal, pero su reacción es opuesta: se va a dormir.

Aquí está la clave: el Sol no sabe quiénes son, ni cuántos hay, ni qué hacen. El Sol está desacoplado de los Observadores. Solo avisa, y ellos deciden cómo reaccionar.

Gallo cantando al amanecer, un ejemplo natural de un 'Observador' reaccionando a un 'Sujeto' (el sol).

2. El Patrón en tu Día a Día: YouTube y las Notificaciones

Este mismo patrón lo usas todos los días. Entras a un canal de YouTube. Te gusta. Te suscribes. Y le das a la campanita.

Anatomía de una Suscripción

En ese momento, acabas de implementar el Patrón Observer:

  • El Sujeto: El Youtuber.
  • El Observador: Tú (y millones de otros fans).
  • El Mecanismo: La plataforma de YouTube actúa como el gestor de la lista de suscripción.

Cuando el Youtuber sube un vídeo (un cambio de estado), la plataforma notifica a todos sus Observadores (te envía una notificación push).

Botón de suscripción y campana de YouTube, un ejemplo real del Patrón Observer en aplicaciones.

3. Implementación en C#: Código Paso a Paso

Traduzcamos esto a C#. Vamos a construir el ejemplo del Youtuber. El código debe ser copiable y funcional.

3.1 Los Contratos (Interfaces)

La interfaz IObservador obliga a cualquier suscriptor a tener un método Actualizar.


/// <summary>
/// El contrato del Observador.
/// Define el método que el Sujeto llamará cuando ocurra un cambio.
/// </summary>
public interface IObservador
{
    void Actualizar(string mensajeDelVideo);
}

La interfaz ISujeto obliga a cualquier publicador a gestionar una lista de suscriptores y notificarles.


/// <summary>
/// El contrato del Sujeto.
/// Define los métodos para gestionar suscriptores y notificarles.
/// </summary>
public interface ISujeto
{
    void Suscribir(IObservador observador);
    void Desuscribir(IObservador observador);
    void Notificar();
}

3.2 El Sujeto Concreto (Clase Youtuber)

Ahora creamos la clase Youtuber que implementa ISujeto. Su "secreto" es simplemente una List<IObservador>.


using System.Collections.Generic;

/// <summary>
/// El Sujeto Concreto. Mantiene una lista de Observadores
/// y les notifica cuando su estado (tituloDelUltimoVideo) cambia.
/// </summary>
public class Youtuber : ISujeto
{
    // La lista de "suscritos con campanita"
    private List<IObservador> suscriptores = new List<IObservador>();
    
    // El "estado" que los observadores quieren saber
    private string tituloDelUltimoVideo = "";

    public void Suscribir(IObservador observador)
    {
        suscriptores.Add(observador);
        Console.WriteLine($"-> [YOUTUBER] ¡Un nuevo suscriptor se ha unido!");
    }

    public void Desuscribir(IObservador observador)
    {
        suscriptores.Remove(observador);
        Console.WriteLine("-> [YOUTUBER] Un suscriptor se ha ido...");
    }

    // El corazón del patrón: notificar a todos en la lista
    public void Notificar()
    {
        Console.WriteLine($"-> [YOUTUBER] Notificando a {suscriptores.Count} suscriptores...");
        foreach (var sub in suscriptores)
        {
            sub.Actualizar(tituloDelUltimoVideo);
        }
    }

    // Método propio del Youtuber que dispara la notificación
    public void SubirVideo(string titulo)
    {
        Console.WriteLine($"\n¡¡NUEVO VIDEO SUBIDO: {titulo}!!");
        this.tituloDelUltimoVideo = titulo;
        Notificar(); // Avisar a todos
    }
}

3.3 El Observador Concreto (Clase Suscriptor)

Finalmente, creamos la clase Suscriptor que implementa IObservador. Aquí es donde definimos la reacción específica al aviso.


/// <summary>
/// El Observador Concreto. Reacciona al aviso del Sujeto
/// implementando el método Actualizar().
/// </summary>
public class Suscriptor : IObservador
{
    private string nombreDeUsuario;

    public Suscriptor(string nombre)
    {
        this.nombreDeUsuario = nombre;
    }

    // Esta es la REACCIÓN específica de este observador
    public void Actualizar(string mensajeDelVideo)
    {
        Console.WriteLine($"   > Hola soy {nombreDeUsuario}! Me llegó el aviso: '{mensajeDelVideo}'");
    }
}

3.4 Ejecutando el Código

Juntamos todo en un Program.cs para ver la magia.


using System;

public class Program
{
    public static void Main(string[] args)
    {
        // 1. Creamos al Sujeto
        Youtuber miCanal = new Youtuber();

        // 2. Creamos a los Observadores
        IObservador fanEneko = new Suscriptor("Eneko_Gamer");
        IObservador fanLola = new Suscriptor("Lola_Art");

        // 3. ¡Se suscriben!
        miCanal.Suscribir(fanEneko);
        miCanal.Suscribir(fanLola);

        // 4. El Youtuber sube un vídeo. Ambos reciben el aviso.
        miCanal.SubirVideo("Aprendiendo Patrones de Diseño");

        // 5. Un nuevo fan se une
        IObservador fanLuis = new Suscriptor("Luis_Dev");
        miCanal.Suscribir(fanLuis);

        // 6. Lola se cansa y se va (Importante: Desuscribirse)
        miCanal.Desuscribir(fanLola);
        
        // 7. El Youtuber sube otro vídeo. Solo Eneko y Luis lo reciben.
        miCanal.SubirVideo("Mi Gato Toca el Piano");
    }
}

Resultado en Consola:


-> [YOUTUBER] ¡Un nuevo suscriptor se ha unido!
-> [YOUTUBER] ¡Un nuevo suscriptor se ha unido!

¡¡NUEVO VIDEO SUBIDO: Aprendiendo Patrones de Diseño!!
-> [YOUTUBER] Notificando a 2 suscriptores...
   > Hola soy Eneko_Gamer! Me llegó el aviso: 'Aprendiendo Patrones de Diseño'
   > Hola soy Lola_Art! Me llegó el aviso: 'Aprendiendo Patrones de Diseño'

-> [YOUTUBER] ¡Un nuevo suscriptor se ha unido!
-> [YOUTUBER] Un suscriptor se ha ido...

¡¡NUEVO VIDEO SUBIDO: Mi Gato Toca el Piano!!
-> [YOUTUBER] Notificando a 2 suscriptores...
   > Hola soy Eneko_Gamer! Me llegó el aviso: 'Mi Gato Toca el Piano'
   > Hola soy Luis_Dev! Me llegó el aviso: 'Mi Gato Toca el Piano'

4. Patrón Observer en Red: SignalR y Aplicaciones Distribuidas

Esto funciona en un solo programa. Pero, ¿qué pasa si Lola y Eneko están en ciudades distintas, conectados a la misma base de datos?

Diferencias entre Local y Remoto

No podemos llamar a un método (Actualizar()) en el PC de Lola desde nuestro servidor en la nube. El Patrón Observer necesita un transporte. En .NET, la herramienta estándar para esto es SignalR, que usa WebSockets por debajo.

Cómo Funciona SignalR

SignalR abstrae el patrón Observer para la red:

  1. Sujeto: El "Hub" de SignalR en tu servidor.
  2. Observadores: Todos los clientes (navegadores, apps móviles) conectados a ese Hub.
  3. Suscribir: Cuando un cliente inicia una conexión con el Hub.
  4. Notificar: Cuando el servidor llama a un método del Hub (ej. Clients.All.SendAsync(...)), SignalR empuja (push) el mensaje a todos los clientes conectados.

Imagina una app de inventario. Cuando Eneko actualiza el stock en la base de datos, el servidor (Sujeto) notifica al Hub, y el Hub de SignalR empuja el nuevo stock a la pantalla de Lola (Observador) al instante.

Diagrama de SignalR implementando el Patrón Observer en .NET para notificar a múltiples clientes desde un servidor.

5. Diagrama UML y Definición Formal

Así es como se ve el patrón en un diagrama de clases UML formal:

Diagrama de clases UML del Patrón Observer, mostrando la relación entre las interfaces ISujeto e IObservador.

Oficialmente, el Patrón Observer:

Es un patrón de diseño de comportamiento que define una dependencia de uno-a-muchos entre objetos. Cuando el estado de un objeto (el Sujeto) cambia, todos sus dependientes (los Observadores) son notificados y actualizados automáticamente.

⚠️ Cuándo NO Usar el Patrón Observer

Observer es potente, pero no es una bala de plata. Usarlo mal puede crear pesadillas de mantenimiento.

  • Evítalo si hay Cadenas de Notificaciones Largas: Si un Observador, al actualizarse, provoca un cambio en otro Sujeto, que a su vez notifica a otros Observadores... has creado una reacción en cadena. Esto es increíblemente difícil de depurar.
  • Complejidad en el Debugging: Cuando tienes 50 observadores suscritos a un solo sujeto, y algo falla, puede ser muy difícil rastrear qué observador causó el problema.
  • Fugas de Memoria (Memory Leaks): Este es el error más común. Si un Observador se suscribe a un Sujeto y olvida Desuscribir, el Sujeto mantendrá una referencia a él, impidiendo que el recolector de basura libere la memoria del Observador.

Alternativas Comunes

  • Patrón Mediator: Si los objetos necesitan comunicarse en ambas direcciones.
  • Event Aggregator: Para suscribirse a *tipos de eventos* en un bus central, en lugar de a un Sujeto específico.
  • Reactive Extensions (Rx.NET): Para flujos de datos y eventos asíncronos muy complejos.

💪 Ejercicio para Practicar

¿Listo para intentarlo? Implementa un sistema de notificaciones de pedidos para un e-commerce:

  1. Sujeto: Crea una clase Pedido que tenga un método ActualizarEstado(string nuevoEstado). Los estados pueden ser "Procesando", "En Camino", "Entregado".
  2. Observadores: Crea 3 observadores:
    • ServicioEmail: Envía un email (simulado con Console.WriteLine) en cada cambio de estado.
    • ServicioSMS: Envía un SMS solo cuando el estado es "En Camino".
    • BaseDatosLog: Registra cada cambio en un log.

Crea un Pedido, suscribe a los 3 servicios y prueba a cambiar el estado varias veces.

Conclusión: La Conexión Invisible

El Patrón Observer es fundamental. Es la conexión invisible que hace que nuestro software se sienta vivo y reactivo.

El Sol sigue sin saber que el gallo canta por él, y tu Youtuber favorito no sabe que existes. Simplemente emiten su cambio, y el patrón se encarga de conectar al que cambia con los que reaccionan.

...

Y ahora que lo sabes... si este patrón te conectó con algo nuevo hoy, ya sabes qué hace esa campanita. 😉