Como utilizar Observers no typescript
Foto de Brennon O.
Por: Brennon O.
10 de Novembro de 2023

Como utilizar Observers no typescript

Como implementar o design patter de Observers em TypeScript de forma elegante

JavaScript typescript Geral Geral Algoritmos

O padrão Observer é muito útil em aplicações Typescript quando precisamos implementar uma comunicação entre objetos, onde um objeto (subject) mantém uma lista de dependências (observers) e notifica todos eles automaticamente sobre qualquer mudança de estado.

Este padrão permite desacoplar as classes, fazendo com que um objeto (subject) não precise saber explicitamente quais objetos dependem dele (observers), precisando apenas notificá-los quando ocorrer alguma mudança.

Neste post vou mostrar como implementar o padrão Observer de forma simples no Typescript, explicando os principais conceitos e vantagens. Veremos na prática como definir uma classe Subject e uma classe Observer, registrar os observers no subject, e notificá-los quando algum evento acontecer.

O padrão Observer é muito comum em frameworks front-end como Angular, e dominar seu uso no Typescript permite escrever códigos mais desacoplados, testáveis e fáceis de manter.

Espero que este artigo possa ajudá-lo a entender e aplicar esse poderoso padrão de projeto nas suas aplicações em Typescript!

O que são Observers e Subjects

O padrão Observer é baseado em duas entidades principais:

Subject (Sujeito) - É o objeto sendo observado. Ele mantém uma lista de observers e notifica todos eles quando ocorre alguma mudança de estado.

Observer (Observador) - É a classe que deseja ser notificada sobre mudanças no Subject. Os observers se registram no subject para receber as notificações.

A ideia é que o Subject e os Observers sejam desacoplados, ou seja, o Subject não precisa saber explicitamente quais Observers dependem dele, apenas mantém uma lista e notifica a todos quando preciso.

Isso traz algumas vantagens:

  • Desacoplamento entre classes
  • Facilidade para adicionar novos Observers
  • Comunicação implícita entre Subject e Observers
  • É possível ter múltiplos Observers observando um mesmo Subject

O Subject normalmente provê métodos para registrar, remover e notificar os Observers. Já o Observer precisa implementar um método de atualização que será chamado pelo Subject quando houver mudanças.

Essa abordagem promove baixo acoplamento e alta coesão, facilitando a manutenção e evolução das classes ao longo do tempo. Por isso o Observer é muito utilizado em aplicações GUI, streams de eventos, e models em MVC.

Por que usar o padrão Observer?

O padrão Observer traz uma série de benefícios que o tornam muito útil em diversas situações:

  • Permite desacoplamento entre classes - O Subject não precisa referenciar explicitamente quais Observers dependem dele. Basta notificá-los quando ocorrer alguma mudança. Isso reduz o acoplamento.
  • Facilita adição de novos Observers - É possível adicionar novos Observers a qualquer momento, sem precisar alterar o Subject ou outros Observers. Basta registrá-los no Subject.
  • Promove reutilização de código - O mesmo Subject pode ser reutilizado com diferentes tipos de Observers, pois eles são desacoplados.
  • Implementa comunicação implícita - O Subject não chama os métodos do Observer diretamente, ele apenas notifica sobre mudanças. Isso promove um desacoplamento maior.
  • Permite multicasting - Um único evento do Subject pode desencadear atualizações em múltiplos Observers. Isso promove reutilização do código do Subject.

Facilita testabilidade - Como as classes são desacopladas, fica mais fácil de testar o Subject e os Observers de forma isolada.

Portanto, o Observer é muito útil quando precisamos implementar uma comunicação entre objetos, mas queremos evitar um alto acoplamento entre eles. É um padrão essencial para quem desenvolve em Typescript.

A classe Subject e o Observer

Para desenvolvermos uma classe de subject capaz de ser reaproveitada, podemos seguir com:

export default class Subject<NotificationParams> {
 
}

Isso trará criará uma classe Subject que espera um parâmetro de tipo com nome NotificationParams.

Com essa classe construída, podemos criar um tipo para nossos observers. Ele precisa ser uma função que recebe um NotificationParams e devolve any (pois o tipo de retorno da função é irrelevante).

Isso poderia ficar assim:

export type Observer<NotificationParams> = (params:NotificationParams) => any;

Com isso feito, podemos criar uma lista de Observers dentro de nossa classe Subject. Essa lista será o coração do nosso patten, pois é nela que vamos armazenar nossos observers.

observers: Observer<NotificationParams>[] = [];

Com essas preparações feitas, podemos criar as 3 funções que regem como essa lista deve ser manipulada. São elas: subscribe, unsubscribe e notify.

Subscribe

Essa função irá receber um observer e adicionala na nossa lista de observadores

subscribe(observer: Observer<NotificationParams>) {
  this.observers.push(observer);
}

Unsubscribe

Essa função irá remover um observer da lista de observadores (como é uma função, podemos fazer chegem por !==, pois sempre será igual a sua execução).

unsubscribe(observer: Observer<NotificationParams>) {
  this.observers = this.observers.filter(_observer => _observer !== observer);
}

Notify

Essa é a função que vai notificar nossos observers, e assim executar nossas funções.

notify(data: NotificationParams) {
  this.observers.forEach(observer => observer(data));
}

 

Como usar nossa classe

Com tudo isso feito, podemos utilizar nossa classe. Como exemplo, podemos pensar em uma classe Store, com uma função addProduct que recebe um nome e um preço.

Essa função vai espera notificar os interessados de que houve um novo produto adicionado a loja.

Primeiro, precisamos criar algo para nossos NotificationParams, nesse caso, um tipo 

NewProductNotification:

type NewProductNotification = {
  name: string;
  price: number;
};

Então, podemos importar nossa classe Subject e utilizala como extends de nossa classe Store:

class Store extends Subject<NewProductNotification> {
  addProduct(name: string, price: number) {
      this.notify({ name, price });
  }
}

export default Store;

E assim, podemos utilizar nossa classe em qualquer lugar que desejarmos, apenas dando subscribe das funções que desejamos que seja executadas.

import Store from "./Store";

const store = new Store();

store.subscribe((product) => {
  console.log(`New product ${product.name} with price ${product.price}`);
});

store.addProduct("Shirt", 100);
store.addProduct("Pants", 200);
store.addProduct("Hat", 300);

Repare que em nenhum momento a Store conhece a função que vai executar, e nem a função sabe que está sendo injetada dentro de Store.

Conclusão

O padrão Observer é uma solução elegante para implementar comunicação desacoplada entre objetos em aplicações Typescript.

Ao manter os subjects e observers separados, evitamos um alto acoplamento, facilitando a manutenção e evolução do código com o passar do tempo.

Vimos como criar uma classe Subject genérica que pode ser estendida e reutilizada em diferentes contextos. Também implementamos os métodos essenciais para registrar, remover e notificar observers.

O exemplo com a classe Store mostra na prática como o padrão Observer pode ser aplicado para notificar sobre novos produtos adicionados, desacoplando completamente a store dos observers interessados nessa notificação.

Dominar padrões como Observer é fundamental para desenvolver códigos mais elegantes, testáveis e flexíveis em Typescript. Eles ajudam a manter uma arquitetura sólida e escalável.

Espero que este artigo tenha sido útil para você entender o funcionamento e as vantagens do padrão Observer. Sinta-se à vontade para aplicá-lo em seus projetos e continuar evoluindo como desenvolvedor Typescript!

Brennon O.
Brennon O.
Ponta Grossa / PR
Responde em 9 h e 8 min
Identidade verificada
1ª hora grátis
5,0
nota média
3
avaliações
R$ 80
por hora
Graduação: Jogos Digitais (Ampli)
Ensino programação de forma prática e focada em seus objetivos. Seja jogos, web, ou qualquer outra. Vamos agendar sua primeira aula gratuíta!

Confira artigos similares

Aprenda sobre qualquer assunto