Como acabar com os IFS ANINHADOS do código? 😱
Um dos maiores desafios na jornada de um desenvolvedor é manter o código legível e desacoplado. Respeitar os princípios e adotar as melhores práticas de desenvolvimento pode ser um grande desafio durante o cotidiano de um profissional, onde existem prazos e diversos fatores que influenciam o mesmo.
Porém, existem técnicas que podem ser adotadas na hora da codificação que permitem solucionar tais problemas com qualidade e padronização, os famosos Design Patterns ou Padrões de Projeto. Um dos Design Patterns que eu particularmente mais gosto é o Strategy, que se enquadra como um Padrão Comportamental. E, aplicando tal padrão, é possível eliminar os ifs aninhados do código de maneira elegante.
Qual o objetivo do Strategy?
Nesse padrão, nós temos objetos que representam uma estratégia e um contexto que faz o intermédio entre o algoritmo e os objetos de estratégia, cujo comportamento vai variar de acordo com a estratégia utilizada.
Para ilustrar melhor o comportamento e o fluxo quando aplicamos o Strategy em um projeto, criei o diagrama abaixo.
Na sua "Classe Inicial", você chama o contexto passando qual estratégia vai ser adotada para a situação em questão, e por sua vez, o contexto vai chamar a estratégia passada. Observe que temos uma Interface de Estratégia, que é implementada em classes concretas, ou seja, essas classes devem implementar o que foi definido no contrato da interface e a implementação pode ser diferente em cada classe. O que significa que estamos aplicando um dos princípios da orientação ao objeto, o Polimorfismo.
Para a situação prática, vou criar uma aplicação do tipo Console, utilizando .NET 7 e C# 11, mas lembre-se, tal padrão pode ser utilizado independente da linguagem ou framework.
Entendendo o Sistema
Estamos criando um sistema para calcular fretes e cada transportadora tem a sua maneira de calcular. Por exemplo, no SEDEX é de um jeito, já na Jadlog é de outra forma, e assim por diante.
Código sem o padrão Strategy:
De maneira geral, passamos por parâmetro para o nosso método CalculateShipping()
qual é a transportadora e nessa função é verificado qual o tipo por if, desviando o fluxo de controle de acordo com o resultado da comparação.
Imagina que agora precisamos adicionar uma nova opção de entrega para cálculo, por Motoboy.. O que teríamos que fazer? Ir no método e adicionar um novo else if para adicionar a nova forma.
E qual é o problema disso? Imagina que a cada vez que precisarmos adicionar ou modificar uma forma de envio, nós teremos que mexer nesse método, e isso acaba ferindo alguns princípios de acoplamento, como o SRP (Princípio da responsabilidade única), e até mesmo o OCP (Principio aberto fechado, onde diz que um método deve estar aberto para extensão mas fechado para modificação).
Executando o programa temos o seguinte resultado no terminal:
Calcular usando DHL
Show! O resultado é o esperado. Mas vamos refatorar esse código utilizando o padrão Strategy?
Passo 1: Criar a estratégia
Na Interface de Estratégia, vamos adicionar o método CalculateShipping()
que precisará ser implementado pelas classes derivadas.
Após a criação da Estratégia, vamos para as classes concretas!
Passo 2: Criar as classes concretas que implementam a estratégia
Com as classes concretas criadas e herdando da Interface que representa a estratégia, nós precisamos implementar o método descrito no contrato.
Com isso, cada objeto terá sua implementação, não importa como a Jadlog implementa - esse detalhe de implementação fica desacoplado do nosso algoritmo sem impactar nas outras formas de envio.
E, agora, precisamos criar o contexto que vai conectar o nosso fluxo de controle com a estratégia.
Passo 3: Criar o Contexto
No contexto, nós definimos um campo privado que representa a estratégia que, por sua vez, é atribuído valor no construtor da classe. Após isso, ao invocar o método CalculateShipping() é chamado o CalculateShipping()
da estratégia passada no construtor, seja Sedex, Jadlog ou DHL.
Vamos ver como fica a chamada na nossa classe principal?
Instanciamos o contexto passando qual estratégia gostaríamos de chamar e, após isso, chamamos o método CalculateShipping()
do contexto que, por sua vez, vai chamar o referente ao objeto passado. Vamos executar a aplicação para verificar o resultado?
Calcular usando DHL
Exatamente o mesmo resultado que antes, porém, agora com um código reutilizável, escalável e respeitando alguns dos princípios para as boas práticas de desenvolvimento de software.
Conclusão
Na minha opinião vale super a pena aplicar o padrão Strategy quando identificar um cenário em que é possível a sua utilização. Como visto, em menos de 5 passos foi possível sua implementação, um esforço nesse momento que pode evitar futuros problemas, obtendo ganhos de legibilidade e menores custos de manutenção.
Obrigado por ter lido até aqui e até o próximo artigo!