Clonagem profunda com JavaScript
em 09 de Novembro de 2023
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:
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:
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>) { |
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>) { |
Notify
Essa é a função que vai notificar nossos observers, e assim executar nossas funções.
notify(data: NotificationParams) { |
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 = { |
Então, podemos importar nossa classe Subject e utilizala como extends de nossa classe Store:
class Store extends Subject<NewProductNotification> { |
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"; |
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!