Menu
Coddy logo textTech

Padrão Observer (Eventos)

Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 54 de 70.

O padrão Observer estabelece uma relação de um-para-muitos onde um objeto (o sujeito) notifica múltiplos objetos dependentes (observadores) quando seu estado muda. No C#, eventos fornecem uma maneira nativa de implementar este padrão de forma elegante.

O padrão envolve um publicador que dispara eventos e assinantes que respondem a eles. Aqui está um exemplo prático de um monitor de preços de ações:

public class Stock
{
    private decimal _price;
    
    public event Action<decimal> PriceChanged;
    
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            PriceChanged?.Invoke(_price);  // Notifica todos os observadores
        }
    }
}

public class PriceAlert
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Alert: Price is now {newPrice}");
    }
}

public class PriceLogger
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Logged: {newPrice}");
    }
}

Os assinantes vinculam seus métodos ao evento usando += e podem desvinculá-los com -=:

var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();

stock.PriceChanged += alert.OnPriceChanged;
stock.PriceChanged += logger.OnPriceChanged;

stock.Price = 150.50m;
// Saída:
// Alerta: Price is now 150.50
// Registrado: 150.50

stock.PriceChanged -= logger.OnPriceChanged;  // Cancelar inscrição
stock.Price = 155.00m;
// Saída: Alerta: Price is now 155.00

O padrão Observer promove o baixo acoplamento - a classe Stock não precisa saber nada sobre seus observadores. Novos observadores podem se inscrever sem modificar o publicador, tornando o sistema fácil de estender e manter.

challenge icon

Desafio

Fácil

Vamos construir um sistema de monitoramento de temperatura usando o padrão Observer com eventos. Você criará um sensor que notifica múltiplos observadores sempre que a temperatura mudar - perfeito para cenários como sistemas de controle climático onde diferentes componentes precisam reagir a atualizações de temperatura.

Você organizará seu código em três arquivos:

  • TemperatureSensor.cs: Crie uma classe TemperatureSensor no namespace Monitoring. Este é o seu publicador - o assunto que outros objetos irão observar. Seu sensor deve ter:
    • Um campo privado para armazenar a temperatura atual
    • Um evento chamado TemperatureChanged que usa Action<double> para notificar os assinantes sobre a nova temperatura
    • Uma propriedade Temperature que dispara o evento sempre que a temperatura for definida para um novo valor
  • Observers.cs: Crie duas classes observadoras no mesmo namespace que assinarão as mudanças de temperatura:
    • DisplayUnit - possui um método OnTemperatureChanged(double temp) que imprime Display: {temp}C
    • AlarmSystem - possui um método OnTemperatureChanged(double temp) que imprime Alarm Check: {temp}C
  • Program.cs: Reúna tudo criando um sensor e ambos os observadores. Assine ambos os observadores no evento do sensor e, em seguida, atualize a temperatura. Depois disso, cancele a assinatura do sistema de alarme e atualize a temperatura novamente para mostrar que apenas a unidade de exibição recebe a notificação.

Você receberá duas entradas:

  • Primeira leitura de temperatura (ex: 22.5)
  • Segunda leitura de temperatura (ex: 28.0)

Seu programa deve:

  1. Criar o sensor e ambos os observadores
  2. Assinar ambos os observadores no evento TemperatureChanged
  3. Definir a temperatura do sensor para o primeiro valor de entrada (ambos os observadores devem ser notificados)
  4. Cancelar a assinatura do AlarmSystem do evento
  5. Definir a temperatura do sensor para o segundo valor de entrada (apenas o display deve ser notificado)

Por exemplo, se as entradas forem 22.5 e 28.0, a saída deve ser:

Display: 22.5C
Alarm Check: 22.5C
Display: 28C

Isso demonstra o poder central do padrão Observer - o sensor não sabe nem se importa com quem o está ouvindo. Os observadores podem assinar e cancelar a assinatura dinamicamente, e o publicador simplesmente notifica quem estiver ouvindo no momento!

Folha de consulta

O padrão Observer estabelece uma relação de um para muitos onde um objeto (o sujeito) notifica múltiplos objetos dependentes (observadores) quando seu estado muda.

No C#, eventos fornecem uma maneira integrada de implementar este padrão. O padrão envolve um publicador (publisher) que dispara eventos e assinantes (subscribers) que respondem a eles.

Implementação Básica de Eventos

Defina um evento usando event Action<T> e invoque-o quando o estado mudar:

public class Stock
{
    private decimal _price;
    
    public event Action<decimal> PriceChanged;
    
    public decimal Price
    {
        get => _price;
        set
        {
            _price = value;
            PriceChanged?.Invoke(_price);  // Notifica todos os observadores
        }
    }
}

Criando Observadores

Observadores são classes com métodos que correspondem à assinatura do evento:

public class PriceAlert
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Alert: Price is now {newPrice}");
    }
}

public class PriceLogger
{
    public void OnPriceChanged(decimal newPrice)
    {
        Console.WriteLine($"Logged: {newPrice}");
    }
}

Inscrevendo e Cancelando a Inscrição

Use += para se inscrever e -= para cancelar a inscrição em eventos:

var stock = new Stock();
var alert = new PriceAlert();
var logger = new PriceLogger();

stock.PriceChanged += alert.OnPriceChanged;    // Inscrever
stock.PriceChanged += logger.OnPriceChanged;   // Inscrever

stock.Price = 150.50m;  // Ambos os observadores notificados

stock.PriceChanged -= logger.OnPriceChanged;   // Cancelar inscrição

stock.Price = 155.00m;  // Apenas o alerta notificado

O padrão Observer promove o baixo acoplamento - o publicador não precisa saber nada sobre seus observadores. Novos observadores podem se inscrever sem modificar o publicador.

Experimente você mesmo

using System;
using Monitoring;

class Program
{
    public static void Main(string[] args)
    {
        // Ler as temperaturas de entrada
        double temp1 = Convert.ToDouble(Console.ReadLine());
        double temp2 = Convert.ToDouble(Console.ReadLine());

        // TODO: Criar uma instância de TemperatureSensor

        // TODO: Criar instâncias de DisplayUnit e AlarmSystem

        // TODO: Inscrever ambos os observadores no evento TemperatureChanged

        // TODO: Definir a temperatura do sensor para temp1 (ambos os observadores notificados)

        // TODO: Cancelar a inscrição do AlarmSystem no evento

        // TODO: Definir a temperatura do sensor para temp2 (apenas o display notificado)
    }
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos