Sobes.tech
Middle

O que é o padrão Observador e em que casos é aconselhável usá-lo?

sobes.tech IA

Resposta da IA

O padrão Observer refere-se a padrões de design comportamentais. Ele define a relação "um para muitos" entre objetos, onde a mudança de estado de um objeto (o sujeito) notifica automaticamente todos os objetos dependentes (observadores) e os atualiza.

Princípio de funcionamento:

O sujeito mantém uma lista de seus observadores. Quando seu estado muda, ele percorre a lista e chama o método de atualização em cada observador. Cada observador implementa uma interface específica que define esse método de atualização. Os observadores podem "inscrever-se" nas atualizações do sujeito e "cancelar a inscrição".

Razões para usar:

  • Para notificar múltiplos objetos sobre uma mudança de estado de um objeto. Isso é típico em aplicações GUI, onde uma mudança no estado de um widget (por exemplo, clicar em um botão) deve atualizar outros elementos da interface.
  • Quando o sistema é composto por múltiplos objetos fracamente acoplados que devem interagir, mas sem conhecer os detalhes de implementação de cada um. O sujeito não conhece as classes específicas dos observadores, apenas que eles implementam a interface de observador.
  • Em sistemas onde um evento em um local deve desencadear ações em vários outros locais. Por exemplo, ao atualizar dados em um servidor, é necessário atualizar a visualização desses dados para todos os usuários conectados.
  • Ao implementar sistemas reativos ou fluxos de dados. Os observadores reagem às mudanças como eventos em um fluxo.

Exemplo de estrutura (Java):

// Interface de observador
public interface Observer {
    void update();
}

// Interface de sujeito
public interface Subject {
    void attach(Observer observer); // Adicionar observador
    void detach(Observer observer); // Remover observador
    void notifyObservers(); // Notificar observadores
}

// Sujeito concreto
public class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int state; // Estado do sujeito

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        notifyObservers(); // Notificar ao mudar o estado
    }

    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

// Observador concreto
public class ConcreteObserver implements Observer {
    private ConcreteSubject subject; // Referência ao sujeito

    public ConcreteObserver(ConcreteSubject subject) {
        this.subject = subject;
        this.subject.attach(this); // Inscrever-se
    }

    @Override
    public void update() {
        // Executar ação ao atualizar,
        // possivelmente obter o estado do sujeito
        int state = subject.getState();
        System.out.println("Observador atualizado. Estado do sujeito: " + state);
    }
}

Vantagens:

  • Baixo acoplamento: O sujeito não conhece as classes específicas dos observadores.
  • Flexibilidade: Fácil adicionar ou remover observadores sem alterar o sujeito.
  • Reutilização: Observadores podem ser usados com diferentes sujeitos.

Desvantagens:

  • A ordem de notificação não é garantida: A ordem de chamada do método update nos observadores pode ser indefinida.
  • Problemas de desempenho: Com muitos observadores, a notificação pode levar tempo.
  • Dificuldade de depuração: É difícil rastrear o fluxo de execução quando muitos objetos reagem a um evento.