Open Closed Principle, também conhecido como Princípio do Aberto Fechado.
Este é o segundo princípio do SOLID e certamente o princípio mais polêmico, desconhecido e não utilizado.
Software entities (classes, modules, functions, etc.) should be open for
extension, but closed for modification
Entidades de software (classes, módulos, funções, etc) devem estar abertas para extensão, mas fechadas para modificação.
Software é evolutivo, raramente um software é feito uma vez e nunca mais será modificado. Sendo assim onde esse princípio tenta chegar?
Extensibilidade
É uma das chaves da orientação a objetos, quando um novo comportamento ou funcionalidade precisar ser adicionado é esperado que as existentes sejam estendidas e e não alteradas, assim o código original permanece intacto e confiável enquanto as novas são implementadas através de extensibilidade. Criar código extensível é uma responsabilidade do desenvolvedor maduro, utilizar design duradouro para um software de boa qualidade e manutenibilidade.
Abstração
Quando aprendemos sobre orientação a objetos com certeza ouvimos sobre abstração, é ela que permite que este princípio funcione. Se um software possui abstrações bem definidas logo ele estará aberto para extensão.
Na prática
Vou usar um exemplo bem simples para podermos entender facilmente como funciona.
Observe esta classe:
public enum TipoDebito { ContaCorrente, Poupanca }
public class Debito
{
public void Debitar(int valor, TipoDebito tipo)
{
if (tipo == TipoDebito.Poupanca)
{
// Debita Poupanca
}
if (tipo == TipoDebito.ContaCorrente)
{
// Debita ContaCorrente
}
}
}
É uma classe de débito em conta que valida o tipo da conta para aplicar a regra de negócio correta para conta corrente e para conta poupança. Agora vamos supor que surgiu um novo tipo de débito em conta (conta investimento), logo seria necessário modificar a classe.
Qual é o problema de um IF a mais?
Se modificarmos a classe colocando mais um IF de validação, além de ter que substituirmos esta classe na publicação da nova versão, corremos o risco de introduzir alguns bugs em uma classe que já estava funcionando.
Além de ter que testar todos os tipos de débito em conta, um bug introduzido nesta modificação não pararia apenas o débito em conta investimento mas poderia causar que todos os tipos de débitos parassem de funcionar.
Não queremos isso certo? Na verdade queremos ter o mínimo de trabalho possível e maior garantia de qualidade.
Como deveria ser?
Vamos para um exemplo de um código usando abstração para gerar extensibilidade:
public abstract class Debito
{
public abstract void Debitar(int valor);
}
public class DebitoContaCorrente : Debito
{
public override void Debitar(int valor)
{
// Debita Conta Corrente
}
}
public class DebitoContaPoupanca : Debito
{
public override void Debitar(int valor)
{
// Debita Conta Poupança
}
}
public class DebitoContaInvestimento : Debito
{
public override void Debitar(int valor)
{
// Debita Conta Investimento
}
}
Veja que possuímos agora uma abstração bem definida, onde todas as extensões implementam suas próprias regras de negócio sem necessidade de modificar uma funcionalidade devido mudança ou inclusão de outra.
O tipo de débito em conta de investimento foi implementado sem modificar nada, usando apenas a extensão. Além de tudo o código está muito mais bonito, entendível e fácil para aplicar cobertura de testes de unidade. Vale mencionar que também está de acordo com o primeiro princípio do SOLID o SRP
Conclusão
Este princípio nos atenta para um melhor design, tornando o software mais extensível e facilitando sua evolução sem afetar a qualidade do que já está desenvolvido.
Para o uso do Open Closed Principle é muito comum utilizarmos o Strategy Pattern do GoF, prometo explicá-lo em outro momento, apenas para não tornar este exemplo muito complexo.
Referências