Open Closed Principle aplicado ao Angular

Andrew Rosário
Inside PicPay
Published in
4 min readSep 30, 2022

O princípio Open Closed ou Aberto/Fechado, é um dos 5 cincos princípios que formam o famoso acrônimo SOLID. Cada letra é representada por um princípio de design de código voltado ao paradigma da Orientação a Objetos.

O Open Closed Principle, defende que as classes devem estar abertas para extensão, mas fechadas para modificação. Temos como ganho uma arquitetura mais flexível, reduzindo drasticamente o impacto nas mudanças realizadas no software.

Vamos tomar como exemplo uma aplicação de vendas, onde temos dois perfis: um administrador que tem acesso completo e o comercial que tem um acesso mais limitado.

Classe VendaService com condições por perfis

No código acima podemos ter alguns problemas de design. A princípio pode parecer simples, mas conforme as regras crescem (por exemplo: inclusão de novos perfis ou até mesmo uma outra regra adicional), mais frágil e suscetível a bugs essa classe se torna. Portanto, aqui estamos quebrando o princípio do Aberto/Fechado.

Podemos refatorar esse código, tornando-o mais coeso. A primeira ideia é criar uma abstração da classe de vendas. Note aqui o ponto mais importante: Vendas NÃO deve conhecer sobre perfis. Deixamos essa classe fechada para modificações.

Classe VendaService, agora de maneira abstrata com somente a assinatura dos métodos

Agora criamos mais duas classes com responsabilidades bem definidas: Uma para venda do perfil administrador e outra para venda do perfil comercial. As duas herdam da classe de vendas (ou poderiam implementar a interface, caso prefira evitar herança) e precisam ter os métodos dela, porém cada uma com sua regra específica.

Classes específicas de venda para cada perfil que herdam de VendaService

Perceba que agora temos a classe de vendas aberta para extensões. Podemos estender em quantos perfis fossem necessários sem quebrar o contrato da classe principal.

É curioso dizer que esses exemplos foram extraídos de um outro artigo que escrevi sobre o Princípio da Inversão de Dependência, letra D do SOLID. Por mais que aqui o princípio seja outro, o conceito por trás de um bom design de código não muda, mostrando que todos os princípios conversam entre si.

Trazendo o cenário para os componentes do Angular

Aproveitando esse mesmo contexto de vendas, vamos imaginar um componente de card que renderiza alguns dados financeiros. Se o perfil for administrador deve mostrar de um jeito, se for comercial deve mostrar de outro. Veremos primeiro a implementação que infringe o Open Closed Principle:

Componente de Card de vendas com condições por perfis

Fica muito claro que temos no componente exatamente o mesmo problema que tínhamos com a classe: várias condições que não deveriam existir. Para nos adequarmos ao princípio, precisamos deixá-lo fechado para modificações, ou seja, remover as condições com *ngIf. Precisamos também encontrar uma forma de deixá-lo aberto para extensão.

Projeção de conteúdo

Vamos entender a Projeção de conteúdo (ou Content Projection) extraindo sua definição da documentação do Angular:

A projeção de conteúdo é um padrão no qual você insere, ou projeta , o conteúdo que deseja usar dentro de outro componente. Por exemplo, você pode ter um componente Card que aceita conteúdo fornecido por outro componente.

Esta é a solução perfeita para aplicarmos o Open Closed Principle. Utilizamos o <ng-content> para esperar um conteúdo a ser projetado que venha de fora. Ganhamos coesão dentro do componente card e ao mesmo tempo flexibilidade para os componentes externos.

Componente de Card de vendas utilizando projeção de conteúdo

Excelente! Trouxemos a mesma prática mostrada anteriormente agora para o contexto de componentes no Angular. O Template HTML está fechado para modificação.

Mas podemos deixar esse código melhor. Em VendaCardComponent temos um Output visible que por sua vez invoca um método para visualizar informações que somente o perfil administrador tem acesso. Mais uma vez não estamos respeitando os princípios do SOLID. Esse código apresenta um forte acoplamento para o componente.

Para limparmos o nosso componente e deixá-lo coeso, primeiramente precisamos expor o Output visible. É neste momento também em que tornamos ele aberto para extensão.

Componente de Card de vendas com Output Emitter

Agora sim eliminamos qualquer regra de perfis dentro do componente de card. O método para visualizar informações privilegiadas poderá ser anexado de forma externa, como por exemplo em uma diretiva.

Diretiva que ouve o evento visible para executar alguma ação

Utilizando o decorator HostListener, podemos ouvir o Output visible que nosso componente de card emite e executar nosso código. É uma prática muito poderosa e pouco conhecida!

Agora podemos adicionar a diretiva apenas onde precisamos. No caso ela será necessária apenas para os componentes de perfil administrador. Os componentes de perfil comercial nem precisam saber da existência desta diretiva.

Componentes específicos de vendas por perfis utilizando a composição

Veja como é possível trazer vários conceitos envolvendo boas práticas para o Angular. Nesse caso temos o conceito da Composição aplicado de forma bastante pragmática.

Trocamos as condições que dificultavam a manutenção no código por um padrão modular. A ideia é que nossos componentes comportem-se como “plugins”, adicionando algo novo sem que isso interfira no comportamento do que já existe.

Solução alternativa com Injeção de Dependência

Podemos ter o mesmo resultado aplicando o Element Injector em nossa diretiva. A vantagem dessa prática é que deixamos mais explícita a fonte do nosso Output para que a função seja aplicada. A desvantagem é que geramos um vínculo da diretiva com o componente de card, perdendo sua flexibilidade e a possibilidade maior de reuso.

Diretiva que obtém a instância do componente de Card via injeção de dependência

Conclusão

Neste artigo podemos entender o Princípio Aberto/Fechado e quais são os benefícios de utilizá-lo em nosso código. Além disso, foi possível aplicar este conceito dentro do contexto do Angular, tirando proveito de seus recursos como a Projeção de Conteúdo e a Injeção de Dependência com diretivas.

--

--

Andrew Rosário
Inside PicPay

Desenvolvedor Front-end, mentor e palestrante. Apaixonado por tecnologia e por compartilhar conhecimento.