SOLID (2/5)
Um dos conceitos mais solicitados hoje no mundo do desenvolvimento é o conhecimento de SOLID. Nesse post, vamos explorar o OCP - Open-Closed Principle.
OCP - Open-Closed Principle
O princípio aberto-fechado diz que um software deve estar aberto para extensão e fechado para modificação. Isso significa que podemos estender o funcionamento de um software adicionando novos módulos, sem precisar modificar o código existente.
Vamos usar como exemplo uma classe responsável por cobranças:
public enum TipoCobranca {
Credito,
Debito
}
public class Cobranca {
public void GerarCobranca(TipoCobranca tipoCobranca, decimal valor){
switch (tipoCobranca){
case TipoCobranca.Credito:
// Lógica de cobrança por crédito
break;
case TipoCobranca.Debito:
// Lógica de cobrança por débito
break;
default:
throw new ArgumentException("Tipo de cobrança inválido");
}
}
}
Imagine que, no futuro, precisemos adicionar uma nova forma de cobrança, como PIX. Para isso, precisaríamos modificar a classe Cobranca
, o que vai contra o princípio aberto-fechado.
A palavra-chave aqui é abstração. Precisamos abstrair a lógica de cobrança para que possamos adicionar novas formas de cobrança sem modificar a classe existente. Para resolver isso, podemos criar uma interface que define o contrato de cobrança e implementar diferentes classes para cada tipo de cobrança:
public interface ICobranca {
void GerarCobranca(decimal valor);
}
public class CobrancaCredito : ICobranca {
public void GerarCobranca(decimal valor) {
// Lógica de cobrança por crédito
}
}
public class CobrancaDebito : ICobranca {
public void GerarCobranca(decimal valor) {
// Lógica de cobrança por débito
}
}
public class CobrancaPix : ICobranca {
public void GerarCobranca(decimal valor) {
// Lógica de cobrança por PIX
}
}
Note que agora, se quisermos adicionar uma nova forma de cobrança (como CobrancaPix
), não precisamos modificar nenhuma classe existente, apenas criar uma nova classe que implementa a interface ICobranca
. Isso mantém o software aberto para extensão e fechado para modificação.
Como usar essas classes?
Para utilizar essas implementações, podemos criar um contexto que trabalhe com a abstração:
public class ProcessadorCobranca {
private readonly ICobranca _cobranca;
public ProcessadorCobranca(ICobranca cobranca) {
_cobranca = cobranca;
}
public void ProcessarPagamento(decimal valor) {
_cobranca.GerarCobranca(valor);
}
}
// Uso:
var cobrancaPix = new CobrancaPix();
var processador = new ProcessadorCobranca(cobrancaPix);
processador.ProcessarPagamento(100.00m);
Dessa forma, o ProcessadorCobranca
não precisa conhecer os detalhes específicos de cada tipo de cobrança, trabalhando apenas com a abstração ICobranca
.