PHP e o Princípio do Aberto/Fechado

Como manter classes abertas para extensão e fechadas para modificação.

Alan Willms
Jul 24, 2015 · 3 min read

No artigo anterior vimos que uma classe só deveria ter uma única responsabilidade ou um único motivo para mudar. Hoje veremos a segunda recomendação do SOLID, chamada de “Princípio do Aberto/Fechado”.

Uncle Bob pegou esse conceito emprestado de Bertrand Meyer, que o define da seguinte maneira:

Entidades de software (classes, módulos, funções, etc.) devem ser abertas para extensão, mas fechadas para modificação.

  1. O comportamento deve estar aberto para mudanças, para atender a novos requisitos do software.
  2. No entanto, o código fonte deve estar fechado para alteração, ou seja, não deveria ser tocado para introduzir esses novos comportamentos.

Além de facilitar a introdução de novos comportamentos, a vantagem é que se você não altera uma entidade existente, você diminui as chances de introduzir novos bugs nela. Mesmo que você tenha uma cobertura total de testes, ainda podem existir casos que os testes não cobrem.

Exemplo: Calculando Impostos

Suponha que mantemos o sistema de uma rede de farmácias. Para tornar o exemplo mais simples, estamos em um Brasil hipotético em que o governo resolveu simplificar o sistema tributário.

A rede quer saber exatamente quanto cada farmácia paga para o governo todos os anos, então o sistema precisa calcular os impostos pagos em cima dos produtos vendidos (remédios, cosméticos, produtos de higiene, etc.) e dos serviços prestados (aplicação de injeção, curativos, etc.).

Nesse sistema tributário simplificado, tudo que se paga hoje é o ICMS sobre os produtos vendidos e o ISS sobre os serviços prestados:

Um belo dia o dono da rede de farmácias anuncia um grande contrato com um fornecedor europeu, e que começará a importar cosméticos estrangeiros.

Infelizmente esse Brasil hipotético ainda cobra impostos sobre mercadoria importada, então precisamos atualizar o nosso sistema para incluir esse novo comportamento:

Imediatamente percebemos que há algo de errado. Além de criar uma nova classe para tratar das regras de negócio de um produto importado, tivemos que mexer também na classe do impostômetro. Essa classe nunca precisou ser alterada antes!

Uma semana depois, recebemos um memorando anunciando que estava sendo estabelecida uma parceria com a Argentina para a exportação de medicamentos manipulados, ou seja, em breve também pagaremos o IE (imposto sobre exportação). Boas notícias para a rede, más notícias para nós, pois agora precisamos alterar o impostômetro novamente!

Como descobrimos o Princípio do Aberto/Fechado, resolvemos nos preparar para a nova mudança e refatorar nossas classes seguindo o design pattern Stategy:

Ótimo! Nossa classe de impostômetro ficou muito mais limpa! Agora será muito mais fácil adicionar o novo comportamento dos produtos exportados sem alterar as classes existentes:

Agora a classe do impostômetro não precisa mais saber quais métodos chamar para somar os impostos. Ela pode receber qualquer novo item que seja criado no futuro e, desde que ele implementa a interface, ela será capaz de somar os impostos adequadamente.

Detectando Violações com Ferramentas

Um dos principais sintomas de que sua classe não está aberta para extensão e fechada para modificação é quando você tem um mesmo conjunto de condições switch ou if que se repete em vários lugares da aplicação, como um teste verificando qual é o "tipo", "nível", "status", etc., de uma entidade.

Para detectar códigos esses conjuntos de condições, você pode executar o PHP Mess Detector e prestar atenção às classes e métodos com alta complexidade ciclomática. Se você ver um bloco semelhante de condições if/switch se repetindo o tempo todo em vários lugares, é provável que você pode refatorar seguindo o princípio do aberto/fechado.

Conclusão

Esse princípio é ótimo para manter o código fácil de alterar, mas evite cair na síndrome da bola de cristal, implementando prematuramente uma abstração só porque acha que talvez ela seja necessária um dia. Pode ser que você nunca precise dela.

Uma dica é só implementar a abstração quando realmente surge uma segunda extensão do comportamento original. Como você não fez isso antes, você terá o trabalho de refatorar o código para criar a abstração que não foi feita no começo, mas desta forma você evita adicionar mais complexidade do que o necessário.

Referências

Tableless

Um lugar para ler e discutir sobre desenvolvimento, design…

Tableless

Um lugar para ler e discutir sobre desenvolvimento, design, web semântica, back-end e outros assuntos relacionados a web. Se você quiser publicar artigos conosco, envie um email: medium[at]tableless.com.br ou *clique no link* http://bit.ly/escreva-tableless-medium

Alan Willms

Written by

Software development nerd. In 💙 with Ruby, PHP, JavaScript, Crystal, and other techy stuff.

Tableless

Um lugar para ler e discutir sobre desenvolvimento, design, web semântica, back-end e outros assuntos relacionados a web. Se você quiser publicar artigos conosco, envie um email: medium[at]tableless.com.br ou *clique no link* http://bit.ly/escreva-tableless-medium