Os padrões de projetos ajudam estruturar programas de maneiras mais flexíveis, fáceis de entender e manter. Através deles sistemas são construídos com boas qualidades de design orientado à objetos(OO). Eles nos fornecem soluções gerais para problemas de projeto. Esse artigo explica o padrão observer mostrando seu uso e estrutura aplicada no contexto de um programa de fórum.

O padrão observer é utilizado para comunicar o estado (dados que os observadores estão interessados) para um conjunto de objetos de uma maneira levemente ligada. Quando dois objetos estão levemente ligados, eles podem se interagir, mas conhecem muito pouco um sobre o outro. Utilizando esse padrão, podemos adicionar ou remover observadores inscritos num Subject sem fazer alterações no programa.

O diagrama de classe do padrão observer é mostrado abaixo:

Forum é a classe concreta que implementa Subject enquanto Observer é uma interface que todos os observadores devem implementar. Cada usuário (observador) registra no fórum para receber atualizações. Essa inscrição ocorre no construtor da classe Usuário que recebe o objeto Forum como argumento no parâmetro desse construtor.

No padrão observer um determinado objeto (Subject) realiza notificações a seus inscritos/interessados e sempre quando existe uma mudança de estado seus assinantes são avisados e atualizados. O Subject é o objeto que contém o estado o controla. Na implementação desse exemplo, os observadores dependem do Subject para receber novas notificações quando o estado do Subject é alterado:

Os seguintes eventos podem ocorrer:

  • Novos usuários podem ser inscrever no fórum para receber as mensagens;
  • Usuários do fórum podem cancelar sua assinatura para não serem mais notificados com novas mensagens do fórum.

Código da classe Forum:

package com.programacao4devs.observer.subject.impl;
import java.util.ArrayList;
import java.util.List;

import com.programacao4devs.observer.interfaces.Observer;
import com.programacao4devs.observer.interfaces.Subject;

public class Forum implements Subject {
	
	private List observers;
	private String message;
	
	public Forum() {
		observers = new ArrayList();
	}

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

	@Override
	public void removeObserver(Observer observer) {
		int index = observers.indexOf(observer);
		if (index >= 0) {
			observers.remove(index);
		}
	}

	@Override
	public void notifyObservers() {
		for (int index = 0; index < observers.size(); index++) {
			Observer observer = (Observer)observers.get(index);
			observer.update(message);
		}
		
	}
	
	public void messageChanged() {
		 notifyObservers();
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
		messageChanged();
	}

}

Código da classe User:

package com.programacao4devs.observer.impl;

import com.programacao4devs.observer.interfaces.Display;
import com.programacao4devs.observer.interfaces.Observer;
import com.programacao4devs.observer.interfaces.Subject;

public class User implements Observer, Display {
	
	private String name;
	private Subject forum;
	
	public User(String name, Subject forum) {
		this.name = name;
		this.forum = forum;
		forum.registerObserver(this);
	}

	@Override
	public void display(String message) {
		System.out.format("Usuário(a): %s, recebeu a seguinte mensagem do forum: %s", name, message );
		System.out.println();
	}

	@Override
	public void update(String message) {
		display(message);
	}
	
	public void unsubscribe() {
		this.forum.removeObserver(this);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

Código da classe SimuladorForum para executar o teste:

package com.programacao4devs.observer;

import com.programacao4devs.observer.impl.User;
import com.programacao4devs.observer.subject.impl.Forum;

public class SimuladorForum {

	public static void main(String[] args) {
		
		Forum forum = new Forum();
		
		User user1 = new User("Ana", forum);
		User user2 = new User("Pedro", forum);
		
		forum.setMessage("Estamos analisando o tema da próxima palestra...");
		forum.setMessage("O tema da palestra será sobre microserviços");
		
		System.out.println();
		user2.unsubscribe();
		System.out.println("Usuário: " + user2.getName() + " se desinscreveu do forum");
		
		System.out.println("Novos usuários entrando no fórum...");
		User user3 = new User("Bob", forum);
		User user4 = new User("Viviane", forum);
		System.out.println();
		
		forum.setMessage("Palestra agendada sobre microserviços no dia 23/11/21 às 19:00");
		
	}

}

Resultado mostrado no Console:

	
Usuário(a): Ana, recebeu a seguinte mensagem do forum: Estamos analisando o tema da próxima palestra...
Usuário(a): Pedro, recebeu a seguinte mensagem do forum: Estamos analisando o tema da próxima palestra...
Usuário(a): Ana, recebeu a seguinte mensagem do forum: O tema da palestra será sobre microserviços
Usuário(a): Pedro, recebeu a seguinte mensagem do forum: O tema da palestra será sobre microserviços

Usuário: Pedro se desinscreveu do forum
Novos usuários entrando no fórum...

Usuário(a): Ana, recebeu a seguinte mensagem do forum: Palestra agendada sobre microserviços no dia 23/11/21 às 19:00
Usuário(a): Bob, recebeu a seguinte mensagem do forum: Palestra agendada sobre microserviços no dia 23/11/21 às 19:00
Usuário(a): Viviane, recebeu a seguinte mensagem do forum: Palestra agendada sobre microserviços no dia 23/11/21 às 19:00
	

Download do código completo em: https://github.com/programacao4devs/padrao-observer

Os princípios OO utilizados no padrão observer:

  • Encapsule o que varia;
  • Dê prioridade à composição em relação à herança;
  • Programa para interface, não para implementação;
  • Utilize baixo acoplamento (objetos levemente ligados).

Inscreva-se!

Inscreva seu nome e email na Programacao4Devs para receber atualizações de novos artigos, tutoriais e dicas.

Paulo Henrique de Morais

Mestre em Ciências da Computação pela Universidade Federal de Santa Catarina e atua no mercado de programação desde 2015.