Open Closed Principle - PauloGustavo72/solid GitHub Wiki
Esse princípio nos diz que nossa classe deve ser aberta para extensão. Ou seja, tem que ser de fácil uso por outras classes. Mas o princípios também diz que ela deve ser fechada para mudança. Isso quer dizer que devemos faze-lá de alguma forma com que não voltemos nela toda hora para alterar um if
, else
ou qualquer coisa que seje.
Veja o código abaixo:
public class CalculadoraDePrecos {
public double calcula(Compra produto) {
TabelaDePrecoPadrao tabela = new TabelaDePrecoPadrao();
Frete correios = new Frete();
double desconto = tabela.descontoPara(produto.getValor());
double frete = correios.para(produto.getCidade());
return produto.getValor() * (1-desconto) + frete;
}
}
public class TabelaDePrecoPadrao {
public double descontoPara(double valor) {
if(valor>5000) return 0.03;
if(valor>1000) return 0.05;
return 0;
}
}
public class Frete {
public double para(String cidade) {
if("SAO PAULO".equals(cidade.toUpperCase())) {
return 15;
}
return 30;
}
}
Tá legal, está tudo funcionando, e está perfeito. Só que agora pensa no seguinte: imagina que o meu software vá crescer. Então, eu não tenho só a tabela de preços padrão. Eu tenho a tabela de preços padrão e a tabela de preços diferenciados. Pra entrega, eu não uso só os correios. Eu uso os correios, ou estou usando uma outra empresa particular também de entrega de produtos. Imagina que a regra cresceu.
Percebemos aqui que, sem pensar em uma estrutura legal, essa classe irá fica cheia de ifs
, segue um exemplo:
public class CalculadoraDePrecos {
public double calcula(Compra produto) {
Frete correios = new Frete();
double desconto;
if (REGRA 1){
TabelaDePrecoPadrao tabela = new TabelaDePrecoPadrao();
desconto = tabela.descontoPara(produto.getValor());
}
if (REGRA 2){
TabelaDePrecoDiferenciada tabela = new TabelaDePrecoDiferenciada();
desconto = tabela.descontoPara(produto.getValor());
}
double frete = correios.para(produto.getCidade());
return produto.getValor() * (1 - desconto) + frete;
}
}
O problema disso é a complexidade. Imagina que eu tenho 10 regras diferentes, vão ser 10 ifs
e possívelmente vou depender de mais classes.
Solução:
-
Abstração: Primeiro vamos criar abstrações para as classes
TabelaDePrecoPadrao
eFrete
, conforme imagens abaixo:public interface TabelaDePreco { double descontoPara(double valor); }
public class TabelaDePrecoPadrao implements TabelaDePreco
public interface ServicoDeEntrega { double para(String cidade); }
public class Frete implements ServicoDeEntrega {
-
Agora fazemos com que a classe
CalculadoraDePrecos
receba as interfaces acima como argumento no construtor:public class CalculadoraDePrecos { public CalculadoraDePrecos(TabelaDePreco tabela, ServicoDeEntrega entrega) { } }
-
Assim conseguimos dizer por exemplo que o método entrega vai ser calculado acima da implementação passada pelo construtor, segue um exemplo:
double frete = entrega.para(produto.getCidade());