Entendendo o padrão Observer!
Falaaaa galera, meu primeiro post aqui será para compartilhar meu entendimento a cerca desse Design Pattern que tanto me fez esquentar a cuca.
(Apenas um adendo: ainda não sou desenvolvedor profissional, portanto peço que se virem algo que esteja errado ou que possa melhorar, me avisem para editar o post).
Mas afinal o que é esse tal Observer?
O padrão Observer é um Design Pattern do tipo comportamental, (padrões que tratam sobre a comunicação entre objetos), ele é muito util quando temos uma situação onde um ou varios objetos (dentro desse Pattern os chamamos de Observers) querem ser "avisados" sobre algo que ocorra com outro objeto (nesse contexto chamamos esse objeto de Subject)
A primeira vez que esbarrei nesse padrão foi num projeto de campo minado onde havia uma classe "Campo" que deveria ser notificada se outras instancias dessa classe fossem "abertas, marcadas, explodissem e etc", porém o exemplo que finalmente fez com que eu entendesse esse conceito foi o seguir:
Imagine o cenario do Youtube, onde temos canais que produzem conteudo, cada canal tem uma lista de inscritos que quer ser notificada quando aquele canal em questão publicar um video novo; assim sempre que um video for publicado naquele canal, o Youtube varre essa lista de inscritos notificando cada um do upload daquele video novo.
Mas como implementar isso na pratica?
De inicio iremos definir uma interface que conterá o metodo de notificação dos interessados:
public interface Observer {
public void notificar(String canal);
}
Depois iremos definir uma classe concreta que irá implementar essa interface, no nosso exemplo do Youtube, será o Inscrito:
public class Inscrito implements Observer{
private String nomeDoInscrito;
@Override
public void notificar(String canal) {
System.out.println(nomeDoInscrito + " há um novo video no canal: " + canal);
}
public Inscrito(String nomeDoInscrito) {
this.nomeDoInscrito = nomeDoInscrito;
}
}
Agora iremos criar a classe que está sendo "ouvida" pelos Observers:
import java.util.ArrayList;
import java.util.List;
public class Canal {
private String nomeDoCanal;
private List<Observer> listaDeInscritos;
public void registrarInscrito(Observer inscrito) {
listaDeInscritos.add(inscrito);
}
public void removerInscrito(Observer inscrito) {
listaDeInscritos.remove(inscrito);
}
public void notificarInscritos() {
for (Observer inscrito: listaDeInscritos
) {
// Repare que o Subject utiliza o proprio metodo dos inscritos para fazer a notificação:
inscrito.notificar(this.getNomeDoCanal());
}
}
public Canal(String nomeDoCanal) {
this.listaDeInscritos = new ArrayList<>();
this.nomeDoCanal = nomeDoCanal;
}
public String getNomeDoCanal() {
return nomeDoCanal;
}
}
Agora vamos implementar um exemplo pratico para ver o funcionamento desse Pattern:
public class Main {
public static void main(String[] args) {
Canal devIniciante = new Canal("Dev Iniciante");
Inscrito gustavo = new Inscrito("Gustavo");
Inscrito karolayne = new Inscrito("Karolayne");
Inscrito franklin = new Inscrito("Franklin");
Inscrito diego = new Inscrito("Diego");
devIniciante.registrarInscrito(gustavo);
devIniciante.registrarInscrito(karolayne);
devIniciante.registrarInscrito(franklin);
devIniciante.registrarInscrito(diego);
devIniciante.notificarInscritos();
}
}
O codigo acima terá uma saída no console exatamente igual a essa:
Gustavo há um novo video no canal: Dev Iniciante
Karolayne há um novo video no canal: Dev Iniciante
Franklin há um novo video no canal: Dev Iniciante
Diego há um novo video no canal: Dev Iniciante
Vantagem de utilizar o Observer?
Geralmente quando utilizamos o padrão Observer, temos uma "convenção" com os metodos a serem implementados que costumam levar os nomes de:
notify( )
: Quando queremos que os observadores sejam notificados.execute( )
: Quando queremos que uma ação seja executada para cada observador.update( )
: Quando queremos que o estado, atributo e entre outros do observador seja alterado.
As vantagens desse Pattern se encontram em poder manipular um grande volume de objetos sempre que o gatilho para isso seja um Evento de Interesse, outro ponto importante é que utilizando do polimorfismo, pelo fato de a classe Observer
ser uma interface, cada classe concreta pode implementar seu metodo para executar da maneira que melhor lhe couber. Por exemplo:
- Uma classe
Cliente
é uma Observer da classePromoção
. - A classe
Produto
também é um Observer da classePromoção
. - Porém a classe
Cliente
a partir da criação de uma nova promoção será notificada da mesma, enquanto que a classeProduto
poderá ter uma alteração no seu preço conforme o desconto da tal promoção.
Espero que os exemplos acima tenham lhe ajudado a entender o funcionamento desse padrão! Como disse anteriormente, sou um dev em transição de carreira, portanto estou aberto a correções e melhorias!