O Observer é um padrão comportamental que define uma dependência um-para-muitos entre objetos, de modo que quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.
- Quando mudanças no estado de um objeto precisam ser refletidas em outros objetos
- Quando um objeto precisa notificar outros objetos sem fazer suposições sobre quem são esses objetos
- Quando você precisa de um acoplamento fraco entre objetos que interagem
- Quando você tem uma estrutura de publicação/assinatura
from abc import ABC, abstractmethod
from typing import List, Set
# Interface do Observer
class Observer(ABC):
@abstractmethod
def atualizar(self, temperatura: float, umidade: float, pressao: float) -> None:
pass
# Interface do Subject
class Subject(ABC):
@abstractmethod
def anexar(self, observer: Observer) -> None:
pass
@abstractmethod
def desanexar(self, observer: Observer) -> None:
pass
@abstractmethod
def notificar(self) -> None:
pass
# Subject Concreto
class EstacaoMeteorologica(Subject):
def __init__(self):
self._observers: Set[Observer] = set()
self._temperatura = 0.0
self._umidade = 0.0
self._pressao = 0.0
def anexar(self, observer: Observer) -> None:
self._observers.add(observer)
def desanexar(self, observer: Observer) -> None:
self._observers.remove(observer)
def notificar(self) -> None:
for observer in self._observers:
observer.atualizar(self._temperatura, self._umidade, self._pressao)
def set_medicoes(self, temperatura: float, umidade: float, pressao: float) -> None:
self._temperatura = temperatura
self._umidade = umidade
self._pressao = pressao
self.notificar()
# Observers Concretos
class DisplayAtual(Observer):
def atualizar(self, temperatura: float, umidade: float, pressao: float) -> None:
print(f"\nDisplay Atual:")
print(f"Temperatura: {temperatura}°C")
print(f"Umidade: {umidade}%")
print(f"Pressão: {pressao}hPa")
class DisplayEstatistico(Observer):
def __init__(self):
self._temperaturas: List[float] = []
self._umidades: List[float] = []
self._pressoes: List[float] = []
def atualizar(self, temperatura: float, umidade: float, pressao: float) -> None:
self._temperaturas.append(temperatura)
self._umidades.append(umidade)
self._pressoes.append(pressao)
print(f"\nDisplay Estatístico:")
print(f"Temperatura Média: {sum(self._temperaturas) / len(self._temperaturas):.1f}°C")
print(f"Umidade Média: {sum(self._umidades) / len(self._umidades):.1f}%")
print(f"Pressão Média: {sum(self._pressoes) / len(self._pressoes):.1f}hPa")
class DisplayPrevisao(Observer):
def __init__(self):
self._ultima_pressao = 0.0
def atualizar(self, temperatura: float, umidade: float, pressao: float) -> None:
print(f"\nDisplay Previsão:")
if pressao > self._ultima_pressao:
print("Melhorando o tempo!")
elif pressao < self._ultima_pressao:
print("Possibilidade de chuva!")
else:
print("Tempo estável")
self._ultima_pressao = pressao
def main():
# Criando a estação meteorológica
estacao = EstacaoMeteorologica()
# Criando os displays
display_atual = DisplayAtual()
display_estatistico = DisplayEstatistico()
display_previsao = DisplayPrevisao()
# Registrando os displays como observers
estacao.anexar(display_atual)
estacao.anexar(display_estatistico)
estacao.anexar(display_previsao)
# Simulando mudanças nas medições
print("Primeira medição:")
estacao.set_medicoes(25.0, 65.0, 1013.0)
print("\nSegunda medição:")
estacao.set_medicoes(26.0, 70.0, 1014.0)
# Removendo um display
print("\nRemovendo display estatístico...")
estacao.desanexar(display_estatistico)
print("\nTerceira medição:")
estacao.set_medicoes(24.0, 75.0, 1012.0)
if __name__ == "__main__":
main()- Estabelece relações entre objetos em tempo de execução
- Promove o baixo acoplamento entre objetos
- Suporta o princípio Aberto/Fechado
- Permite enviar dados para vários objetos de uma só vez
- Facilita a manutenção e extensão do código
- Os observers podem ser notificados em ordem aleatória
- Se não implementado corretamente, pode levar a problemas de desempenho
- Pode causar vazamentos de memória se os observers não forem desanexados corretamente
- Pode ser difícil rastrear mudanças em sistemas complexos
- Considere usar uma estrutura de dados apropriada para armazenar os observers
- Implemente mecanismos de desanexar observers para evitar vazamentos de memória
- Considere usar variantes do padrão como Push vs Pull
- Pense em como lidar com exceções durante as notificações
- Em sistemas distribuídos, considere a possibilidade de notificações assíncronas