Padrão Observer
Parte da seção Programação Orientada a Objetos do Journey de PHP da Coddy — lição 73 de 91.
O Observer Pattern é um padrão de projeto comportamental que estabelece uma relação de um-para-muitos entre objetos. Quando um objeto (o subject) muda de estado, todos os seus dependentes (os observers) são notificados e atualizados automaticamente.
Pense em uma assinatura de newsletter. A newsletter é o assunto, e os assinantes são os observadores. Quando uma nova edição é publicada, todos os assinantes a recebem automaticamente, sem que a newsletter precise conhecer os detalhes de cada assinante.
Aqui está uma implementação básica em PHP:
<?php
interface Observer {
public function update(string $message): void;
}
class Newsletter {
private array $subscribers = [];
public function subscribe(Observer $observer): void {
$this->subscribers[] = $observer;
}
public function publish(string $content): void {
foreach ($this->subscribers as $subscriber) {
$subscriber->update($content);
}
}
}
class EmailSubscriber implements Observer {
public function __construct(private string $email) {}
public function update(string $message): void {
echo "Sending to {$this->email}: $message\n";
}
}
$newsletter = new Newsletter();
$newsletter->subscribe(new EmailSubscriber("alice@example.com"));
$newsletter->subscribe(new EmailSubscriber("bob@example.com"));
$newsletter->publish("New article released!");
Saída:
Sending to alice@example.com: New article released!
Sending to bob@example.com: New article released!O padrão desacopla o assunto de seus observadores. A classe Newsletter não precisa saber se está notificando assinantes de e-mail, assinantes de SMS ou serviços de notificação push. Ela simplesmente chama update() em qualquer coisa que implemente a interface Observer. Isso torna a adição de novos tipos de observadores simples, sem a necessidade de modificar o código existente.
Desafio
FácilVamos construir um sistema de alerta de preços de ações usando o Observer Pattern. Quando o preço de uma ação muda, todos os investidores registrados devem ser notificados automaticamente — uma aplicação perfeita deste padrão no mundo real.
Você organizará seu código em quatro arquivos:
Observer.php— Defina uma interfaceObservercom um único métodoupdate(string $stockName, float $price): void. Este é o contrato que todos os observadores devem seguir para receber notificações.Investors.php— Inclua o arquivo Observer e crie duas classes que implementam a interface:EmailInvestor— Recebe um endereço de e-mail em seu construtor (use promoção de construtor). Seu métodoupdate()imprime"Email to {$this->email}: {$stockName} is now \${$price}\n"SmsInvestor— Recebe um número de telefone em seu construtor. Seu métodoupdate()imprime"SMS to {$this->phone}: {$stockName} is now \${$price}\n"
Stock.php— Inclua o arquivo Observer e crie uma classeStockque atua como o sujeito. Sua ação deve:- Armazenar o nome da ação e o preço atual (use promoção de construtor para o nome, inicialize o preço como
0.0) - Manter um array privado de observadores
- Ter um método
subscribe(Observer $observer): voidpara adicionar observadores - Ter um método
setPrice(float $price): voidque atualiza o preço e notifica todos os observadores
update()de cada observador com o nome da ação e o novo preço.- Armazenar o nome da ação e o preço atual (use promoção de construtor para o nome, inicialize o preço como
main.php— Inclua os arquivos Investors e Stock. Você receberá três entradas: um nome de ação, um endereço de e-mail e um número de telefone.Crie um
Stockcom o nome fornecido. Inscreva umEmailInvestorcom o endereço de e-mail e umSmsInvestorcom o número de telefone.Defina o preço da ação para
150.50para disparar as notificações para todos os assinantes.
O Observer Pattern mantém sua classe Stock desacoplada dos métodos de notificação específicos. Você poderia facilmente adicionar um PushInvestor ou SlackInvestor mais tarde sem modificar a classe Stock.
Folha de consulta
O Padrão Observer estabelece uma relação de um-para-muitos onde um sujeito notifica automaticamente todos os observadores quando seu estado muda.
Componentes Chave:
- Interface Observer — Define o contrato para observadores com um método
update() - Sujeito (Subject) — Mantém uma lista de observadores e os notifica sobre mudanças de estado
- Observadores Concretos — Implementam a interface Observer para receber notificações
Implementação Básica:
<?php
interface Observer {
public function update(string $message): void;
}
class Subject {
private array $observers = [];
public function subscribe(Observer $observer): void {
$this->observers[] = $observer;
}
public function notify(string $data): void {
foreach ($this->observers as $observer) {
$observer->update($data);
}
}
}
class ConcreteObserver implements Observer {
public function __construct(private string $name) {}
public function update(string $message): void {
echo "Notifying {$this->name}: $message\n";
}
}
$subject = new Subject();
$subject->subscribe(new ConcreteObserver("Observer1"));
$subject->subscribe(new ConcreteObserver("Observer2"));
$subject->notify("State changed!");
Benefícios:
- Desacopla o sujeito de seus observadores
- Fácil de adicionar novos tipos de observadores sem modificar o código existente
- Suporta inscrição/cancelamento de inscrição dinâmica
Experimente você mesmo
<?php
require_once 'Investors.php';
require_once 'Stock.php';
// Ler entradas
$stockName = trim(fgets(STDIN));
$email = trim(fgets(STDIN));
$phone = trim(fgets(STDIN));
// TODO: Criar uma Stock com o nome fornecido
// TODO: Inscrever um EmailInvestor com o endereço de e-mail
// TODO: Inscrever um SmsInvestor com o número de telefone
// TODO: Definir o preço da ação para 150.50 para disparar notificações
?>Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave $thisMétodosPropriedadesConstrutor (__construct)Destrutor (__destruct)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Palavra-chave parent::Sobrescrita de MétodoA Palavra-chave finalClasses AbstratasRecapitulação - Hierarquia de Funcionários7Encapsulamento
Público, Protegido, PrivadoModificadores de Acesso em DetalhesGetters e SettersOcultação de InformaçãoPromoção de Construtor (8.0)Recapitulação - Sistema de Registro de Alunos10Conceitos Avançados de POO
Composição vs HerançaInjeção de DependênciaClasses AnônimasEnums (PHP 8.1)Fibers (PHP 8.1)Mergulho Profundo em Clonagem de ObjetosGeradores e Iteradores13Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository2Namespaces e Autoloading
Introdução a NamespacesA Palavra-chave usePadrão de Autoloading PSR-4Autoloader do ComposerRecapitulação - Projeto Organizado5Interfaces e Contratos
Introdução a InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesInterface vs Classe AbstrataType Hinting com InterfacesRecapitulação - Calculadora de Formas8Métodos Mágicos
Introdução aos Métodos Mágicos__toString e __debugInfo__get, __set, __isset, __unset__call e __callStatic__clone e Clonagem de Objetos__serialize e __unserializeRecapitulação - Coleção Personalizada11Sistema de Tipos e Tratamento de Erros
Declarações de TiposTipos AnuláveisTipos de União e InterseçãoClasses de ExceçãoHierarquia de Exceções PersonalizadasTry, Catch, FinallyRecapitulação - Validador de Formulário14Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário3Propriedades de Classe
Propriedades de Instância vs EstáticasConstantes em ClassesMétodos e Propriedades EstáticosPropriedades Privadas e ProtegidasPropriedades Readonly (PHP 8.1)Recapitulação - Gerenciador de Conta Bancária6Polimorfismo
Sobrescrita de Métodos RevisitadaPolimorfismo via InterfacesType Hinting e Union TypesLate Static BindingRecapitulação - Processador de Pagamentos9Traits
Introdução a TraitsUsando Múltiplos TraitsResolução de Conflitos de TraitsMétodos Abstratos em TraitsTraits vs Herança12Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy