1. O que é o Padrão Observer

O Padrão Observer é um padrão de design comportamental usado para estabelecer uma dependência de um para muitos entre objetos. Especificamente, quando um objeto (referido como o sujeito ou observável) muda, todas as suas dependências (chamadas observadores) são notificadas e atualizadas automaticamente. Este padrão permite um acoplamento frouxo entre sujeitos e observadores, alcançando assim o desacoplamento e flexibilidade entre os objetos.

2. Características e Vantagens do Padrão Observer

O Padrão Observer possui as seguintes características e vantagens:

  • Acoplamento frouxo entre sujeitos e observadores, onde o sujeito não precisa conhecer os detalhes de implementação específicos dos observadores.
  • Adição e remoção dinâmicas de observadores, tornando o sistema mais flexível.
  • Segue o princípio aberto-fechado entre sujeitos e observadores, possibilitando extensão independente e reutilização.
  • Pode estabelecer uma relação de dependência de um para muitos, onde um sujeito pode ter vários observadores.

3. Exemplos de Aplicações Práticas do Padrão Observer

O Padrão Observer possui muitas aplicações práticas na vida real, tais como:

  • Mecanismo de tratamento de eventos em interfaces GUI, como o tratamento de ações quando um botão é clicado.
  • Cotações em tempo real do mercado de ações.
  • Notificação de atividades promocionais em plataformas de e-commerce.

4. Implementação do Padrão Observer em Golang

4.1 Diagrama de Classes UML

Padrão Observer em Golang

4.2 Introdução do Exemplo

Neste exemplo, temos um sujeito (Subject) e dois observadores (ObserverA e ObserverB). O sujeito pode registrar, remover e notificar observadores.

4.3 Etapas de Implementação

4.3.1 Criar Interface do Sujeito e Classe de Sujeito Concreto

type Subject interface {
    RegisterObserver(observer Observer)
    RemoveObserver(observer Observer)
    NotifyObservers()
}

type ConcreteSubject struct {
    observers []Observer
}

// Registrar objeto ouvinte
func (subject *ConcreteSubject) RegisterObserver(observer Observer) {
    subject.observers = append(subject.observers, observer)
}

// Remover objeto ouvinte
func (subject *ConcreteSubject) RemoveObserver(observer Observer) {
    for i, obs := range subject.observers {
        if obs == observer {
            subject.observers = append(subject.observers[:i], subject.observers[i+1:]...)
            break
        }
    }
}

// Acionar notificação de evento
func (subject *ConcreteSubject) NotifyObservers() {
    for _, observer := range subject.observers {
        observer.Update()
    }
}

4.3.2 Criar Interface de Observador e Classes de Observador Concreto

type Observer interface {
    Update()
}

type ConcreteObserverA struct {}

func (observer *ConcreteObserverA) Update() {
    fmt.Println("O Observador A foi notificado.")
}

type ConcreteObserverB struct {}

func (observer *ConcreteObserverB) Update() {
    fmt.Println("O Observador B foi notificado.")
}

4.4 Demonstração do Código de Exemplo

func main() {
    subject := &ConcreteSubject{}
    observerA := &ConcreteObserverA{}
    observerB := &ConcreteObserverB{}

    subject.RegisterObserver(observerA)
    subject.RegisterObserver(observerB)

    subject.NotifyObservers()

    subject.RemoveObserver(observerA)

    subject.NotifyObservers()
}

Saída:

O Observador A foi notificado.
O Observador B foi notificado.
O Observador B foi notificado.

O código de exemplo acima demonstra a implementação específica do padrão observer. O sujeito (ConcreteSubject) registrou dois observadores (ObserverA e ObserverB) e então notificou esses dois observadores. Depois disso, o observador A foi removido do sujeito e o observador B restante foi notificado novamente.