Como Implementar Design Patterns em TypeScript: Observer

Amir Elemam
4 min readAug 13, 2023

--

O que é o Design Pattern Observer?

O padrão Observer é um padrão de design de software que estabelece uma dependência um-para-muitos (one-to-many) entre objetos, de modo que, quando um objeto muda de estado, todos os seus dependentes são notificados e atualizados automaticamente.

É frequentemente usado em sistemas de manipulação de eventos. Por exemplo, você pode encontrá-lo em bibliotecas GUI, onde pode ser usado para atualizar vários elementos em uma tela em resposta à entrada do usuário.

Aqui está uma visão geral muito básica de como funciona:

Assunto: Este é o objeto que contém os dados. Ele mantém uma lista de observadores e possui métodos para adicionar, remover e notificar observadores.
Observadores: Estes são os objetos que ‘observam’ ou observam o sujeito. Eles têm um método que é chamado quando o assunto muda.

Aqui está um pouco mais detalhado:

  1. Quando um assunto muda, ele envia uma notificação a todos os observadores que se registraram nele.
  2. Cada observador tem uma resposta específica para a notificação, que é definida em seu método de atualização. Isso pode estar atualizando seu próprio estado, acionando um processo ou qualquer outra coisa que precise fazer.
  3. Os observadores podem se registrar ou cancelar o registro com o assunto, para que possam parar ou começar a receber notificações.

O padrão Observer tem várias vantagens, especialmente em termos de manter seu código organizado, sustentável e escalável. Aqui estão os principais:

Loose Coupling: O padrão Observer permite que você crie um relacionamento entre objetos sem torná-los fortemente acoplados. O sujeito não precisa saber nada sobre os observadores, a não ser que implementem uma interface específica. Esse acoplamento frouxo leva a um código mais fácil de manter e estender.

Relacionamentos Dinâmicos: Observadores podem ser adicionados ou removidos em tempo de execução. Isso torna o padrão flexível e capaz de lidar com os requisitos dinâmicos do sistema.

Broadcast Communication: O padrão Observer permite que um objeto (o sujeito) transmita mudanças para um número desconhecido de objetos observadores. Isso é diferente de chamar métodos em uma lista fixa de objetos e pode ser especialmente útil em sistemas distribuídos ou simultâneos.

Abstração dos Componentes Centrais: Os componentes Subject e Observer são abstraídos, o que significa que as implementações concretas podem variar. Isso torna o sistema mais flexível e adaptável a mudanças.
Encorajando a arquitetura limpa: como o padrão Observer incentiva a criação de objetos com papéis distintos e claros (ou seja, o “sujeito” que é observado e os “observadores” que reagem às mudanças), é propício para um código mais limpo e modular.

No entanto, também é importante observar que o padrão Observer, como qualquer outro padrão, não deve ser usado em todas as situações. É perfeito para cenários em que uma alteração em um objeto requer alterações em outros e onde o número de objetos que precisam ser alterados não é conhecido ou é dinâmico. Em outras situações, pode ser um exagero e potencialmente introduzir complexidade desnecessária.

Como implementar Observer?

Vamos implementar uma versão muito simples do padrão Observer usando TypeScript. Faremos uma estação meteorológica que informa as mudanças de temperatura para vários elementos de exibição.

Primeiro, vamos definir as interfaces Observer (Observador) e Subject (Assunto):

interface Observer {
update(temp: number): void;
}

interface Subject {
registerObserver(o: Observer): void;
removeObserver(o: Observer): void;
notifyObservers(): void;
}

Agora, vamos criar nossa classe WeatherStation concreta (o Subject) que pode registrar observadores, removê-los e notificá-los sobre alterações:

class WeatherStation implements Subject {
private temperature: number;
private observers: Observer[] = [];

registerObserver(o: Observer) {
this.observers.push(o);
}

removeObserver(o: Observer) {
const index = this.observers.indexOf(o);
if (index > -1) {
this.observers.splice(index, 1);
}
}

notifyObservers() {
for(let observer of this.observers) {
observer.update(this.temperature);
}
}

setTemperature(temp: number) {
console.log('WeatherStation: new temperature measurement: ' + temp);
this.temperature = temp;
this.notifyObservers();
}
}

Agora, vamos criar algumas classes Observer concretas, TemperatureDisplay e Fan, que reagirão às mudanças de temperatura:

class TemperatureDisplay implements Observer {
private subject: Subject;

constructor(weatherStation: Subject) {
this.subject = weatherStation;
weatherStation.registerObserver(this);
}

public update(temp: number) {
console.log('TemperatureDisplay: I need to update my display');
// Here you would update the display with the new temperature value
}
}

class Fan implements Observer {
private subject: Subject;

constructor(weatherStation: Subject) {
this.subject = weatherStation;
weatherStation.registerObserver(this);
}

public update(temp: number) {
if(temp > 25) {
console.log('Fan: It’s hot here, turning myself on...');
// Here you would add the code to turn the fan on
} else {
console.log('Fan: It’s nice and cool, turning myself off...');
// Here you would add the code to turn the fan off
}
}
}

Por fim, vamos usar essas classes:

let weatherStation = new WeatherStation();

let tempDisplay = new TemperatureDisplay(weatherStation);
let fan = new Fan(weatherStation);

weatherStation.setTemperature(20);
weatherStation.setTemperature(30);

Neste exemplo, TemperatureDisplay e Fan são observadores de WeatherStation. Quando a temperatura da estação meteorológica muda, ela notifica todos os observadores cadastrados. O TemperatureDisplay atualiza sua exibição e o Fan liga ou desliga dependendo da temperatura.

Em resumo, o padrão Observer é uma maneira de criar um sistema dinâmico e automatizado de objetos que respondem a mudanças em outros objetos. Esse padrão se enquadra na categoria de padrões de design “comportamentais”, pois lida com a comunicação entre objetos.

--

--