Por um lado, grandes aglomerados de if/else
blocos não são facilmente testáveis . Cada nova "ramificação" adiciona outro caminho de execução e, portanto, aumenta a complexidade ciclomática . Se você quiser testar seu código completamente, precisará cobrir todos os caminhos de execução, e cada condição exigiria que você escrevesse pelo menos mais um teste (supondo que você escreva pequenos testes focados). Por outro lado, as classes que implementam estratégias normalmente expõem apenas um método público, fácil de testar.
Portanto, com o aninhado, if/else
você terminará com muitos testes para uma única parte do seu código, enquanto com o Strategy terá poucos testes para cada uma das várias estratégias mais simples. Com o último, é fácil ter uma cobertura melhor, porque é mais difícil perder os caminhos de execução.
Quanto à extensibilidade , imagine que você esteja escrevendo uma estrutura, na qual os usuários possam injetar seu próprio comportamento. Por exemplo, você deseja criar algum tipo de estrutura de cálculos de impostos e deseja oferecer suporte a sistemas tributários de diferentes países. Em vez de implementar todos eles, você só quer dar aos usuários da estrutura a chance de fornecer uma implementação de como calcular alguns impostos específicos.
Aqui está o padrão de estratégia:
- Você define uma interface, por exemplo
TaxCalculation
, e sua estrutura aceita instâncias desse tipo para calcular impostos
- Um usuário da estrutura cria uma classe que implementa essa interface e a passa para sua estrutura, fornecendo assim uma maneira de executar parte dos cálculos
Você não pode fazer o mesmo com isso if/else
, porque isso exigiria a alteração do código da estrutura, caso em que não seria mais uma estrutura. Como as estruturas são frequentemente distribuídas na forma compilada, essa pode ser a única opção.
Ainda assim, mesmo se você escrever algum código comum, a Strategy é benéfica porque torna suas intenções mais claras. Ele diz "essa lógica é conectável e condicional", ou seja, pode haver várias implementações que podem variar dependendo das ações do usuário, configuração ou mesmo da plataforma.
O uso do padrão Estratégia pode melhorar a legibilidade porque, enquanto uma classe que implementa alguma estratégia específica normalmente deve ter um nome descritivo, por exemplo USAIncomeTaxCalculator
, os if/else
blocos são "sem nome", na melhor das hipóteses, apenas comentados e os comentários podem mentir. Além disso, pelo meu gosto pessoal, apenas ter mais de 3 if/else
blocos seguidos não é legível e fica muito ruim com blocos aninhados.
O princípio Aberto / Fechado também é muito relevante, porque, como descrevi no exemplo acima, o Strategy permite que você estenda uma lógica em algumas partes do seu código ("aberto para extensão") sem reescrever essas partes ("fechado para modificação" )