Define uma relação de um-para-muitos entre objetos em que se o objeto principal (Subject) mudar seu estado as suas dependências (Observers) são notificadas e atualizadas automaticamente.
O pattern Observer define como estabelecer a relação entre os objetos. As chaves deste pattern são o Subject e os Observers. O Subject deve ter uma ou várias dependências de Observers. Todos os Observers são notificados sempre que o Subject mudar seu estado.
O Subject é um publicador de notificações, ele envia as notificações para todos os objetos que o Assinam.
Use o padrão Observer em qualquer uma das seguintes situações:
- Quando uma abstração possui dois aspectos e um depende do outro. Encapsular esses aspectos em objetos separados permite a variação e a reutilização de forma independentes;
- Quando a mudança em um objeto requer a troca de estado em outros e não se sabe quantos objetos precisam ser alterados;
- Quando um objeto deve ser capaz de notificar outros objetos sem saber quem eles são. Em outras palavras, os objetos não podem ser fortemente acoplados.
A estrutura do Observer é representada no seguinte diagrama:

Subject (ISubject)
É a interface que fornece os métodos de incluir, deletar e notificar os Observers.
Observer (IObserver)
Esta interface tem apenas um método (Update). Todos os objetos que querem ser notificados devem implementar esta interface.
ConcreateSubject (Subject)
É a implementação do ISubject. Aqui vamos criar as implementações para incluir, deletar e notificar os objetos Observers.
ConcreateObserver (Observer)
Qualquer objeto pode implementar o IObserver. Ao fazer esta implementação, toda vez que o Subject for alterado os objetos que o implementam serão notificados e alterados.
Vamos ao código de exemplo:
Neste exemplo temos uma classe Cotação Atual, ela será nosso Subject e será responsável por enviar as alterações para todos os Observers.
public class CurrencyQuote : ISubject
{
private readonly List<IObserver> _observers;
private decimal _dollar;
private decimal _euro;
private decimal _pound;
public CurrencyQuote()
{
_observers = new List<IObserver>();
}
public void RegisterObserver(IObserver observer)
{
_observers.Add(observer);
}
public void RemoveObserver(IObserver observer)
{
var i = _observers.IndexOf(observer);
if (i > -1)
{
_observers.RemoveAt(i);
}
}
public void NotifyObservers()
{
foreach (var item in _observers)
{
item.Update(_dollar, _euro, _pound);
}
}
public void Changed()
{
NotifyObservers();
}
public void SetValues(decimal dollar, decimal euro, decimal pound)
{
_dollar = dollar;
_euro = euro;
_pound = pound;
Changed();
}
}
Como podemos ver, esta classe é responsável por orquestrar os estados das propriedades Dolar, Euro e Libra. Além de registrar os Observers e e notificar qualquer mudança que um de seus estados sofra.
Agora, vamos criar 3 Observers que assinarão este Subject. Cada Observer tratará a notificação de forma particular. Lembre-se, o que o Observer faz é implícito para o Subject. Ele apenas notifica e seus Observers fazem o que precisam fazer.
Trader 1
O Observer Trader1 recebe a notificação de mudança e exibe todas as cotações na tela:
public class Trader1 : IObserver
{
private decimal _dollar;
private decimal _euro;
private decimal _pound;
public Trader1(ISubject subject)
{
subject.RegisterObserver(this);
}
public void Update(decimal dollar, decimal euro, decimal pound)
{
_dollar = dollar;
_euro = euro;
_pound = pound;
Display();
}
public void Display()
{
Console.WriteLine($"Cotação atual Dolar: R${_dollar}, Euro R${_euro} e Libra: R${_pound}");
}
}
Trader 2
O Observer Trader2 recebe a notificação de mudança, mas ele não trabalha com a moeda Libra, então, simplesmente a ignora:
public class Trader2 : IObserver
{
private decimal _dollar;
private decimal _euro;
public Trader2(ISubject subject)
{
subject.RegisterObserver(this);
}
public void Update(decimal dollar, decimal euro, decimal pound)
{
_dollar = dollar;
_euro = euro;
Display();
}
public void Display()
{
Console.WriteLine($"Cotação atual Dolar: R${_dollar} e Euro R${_euro}");
}
}
Envia email da cotação
O Observer SendQuoteEmail recebe a notificação de mudança e possui outro comportamento. Ele não exibe na tela, ele envia por e-mail para os investidores toda vez que a cotação muda.
public class SendQuoteEmail : IObserver
{
private decimal _dollar;
private decimal _euro;
private decimal _pound;
public SendQuoteEmail(ISubject subject)
{
subject.RegisterObserver(this);
}
public void Update(decimal dollar, decimal euro, decimal pound)
{
_dollar = dollar;
_euro = euro;
_pound = pound;
SendEmail();
}
public void SendEmail()
{
Console.WriteLine($"Este observer envia as informações por e-mail");
// SMTP IMPLEMENTATION
}
}
Na classe Program, vamos criar o Subject, assinar os Observers e alterar o estado do Subject. Veja que basta alterar o estado do Subject que todos os Observers são comunicados e fazem suas respectivas tarefas.
class Program
{
static void Main(string[] args)
{
var quote = new CurrencyQuote();
var trader1 = new Trader1(quote);
var trader2 = new Trader2(quote);
var sendQuoteEmail = new SendQuoteEmail(quote);
quote.SetValues(3.74M, 4.26M, 4.74M);
Console.WriteLine("====================================");
quote.SetValues(3.61M, 4.17M, 4.65M);
Console.WriteLine("====================================");
quote.SetValues(3.58M, 4.09M, 4.44M);
Console.ReadKey();
}
}

Este código está disponível no meu GitHub.