Olá, tudo bem ?
A ideia nesse caso é mais analisar a diferença entre o retorno x lançamento de exceção
do que do if
. O if
continua sendo necessário na sua lógica, por exemplo, pra perguntar se existe saldo pra poder sacar.
O problema em está na forma como o método saca sinaliza a quem o chamou se deu certo ou não a operação.
Imagine o seguinte cenário com a mesma classe Conta:
public class Conta {
...
public boolean saca(double valor) {
if (this.saldo >= valor) { // se tem saldo
this.saldo -=valor; // faz o débito
return true; // fala que deu certo
}
return false; // senão fala que deu errado
}
}
Agora vemos um teste:
public class TesteConta {
public static void main(String[] args) {
Conta conta = new Conta(500); // conta com saldo 500
Caixa caixaEletronico = new Caixa(); // objeto que representa a máquina com suas funções de emissão e recebimento de cédulas
conta.saca(100); // saca 100, pois tem saldo
caixaEletronico.emite(100); // emite 100
}
}
Contudo, as chamadas para essas operações não podem ocorrer de forma natural. Imagine se o valor solicitado fosse maior que o saldo. Nesse cenário não seria feito o débito, mas emitiria o dinheiro mesmo assim. Problema! Logo precisamos que a emissão só seja feita em caso de sucesso de saque. (Agora entra nossa análise)
Poderiamos pensar num if
, basta verificar o retorno boolean do saca e saberemos se deu certo ou não, assim:
public class TesteConta {
public static void main(String[] args) {
Conta conta = new Conta(500);
Caixa caixaEletronico = new Caixa();
if( conta.saca(700) ) { // tentamos sacar 700, e como não tem saldo
caixaEletronico.emite(700); // essa linha não será executada
}
}
}
Parece problema resolvido. Mas nem sempre é assim. Quem nos obriga a fazer o if e pegar o retorno antes de chamar o emite
? Como o programador que vai usar sua classe Conta e chamar o saca sabe que é necessário esse cuidado ?
As vezes o código que funciona tem problemas ocultos. Acredite, em algum momento do projeto alguém vai chamar o saca e esquecer de fazer qualquer verificação de retorno (que não é obrigatório, a compilação não checa). =/
A ideia é não usar o retorno dos métodos como forma de dizer a terceiros se seu processamento deu certo ou não. E sim usar as Exceptions pra isso.
Veja:
public class Conta {
public void saca(double valor) { // volta a ser void
if (this.saldo >= valor) { // se tem saldo suficiente
this.saldo -=valor; // então faz o débito e termina o trabalho do método
} else { // senão, temos uma exceção à regra de negócio
throw new IllegalArgumentException("Saldo insuficiente");
// lançamos exceção notificando terceiros sobre o problema
}
}
}
O que muda? Agora caso o teste chame o método saca em casos onde a conta não tem saldo, ele não precisa lembrar de fazer a validação do retorno do método. O próprio método saca lança um alerta de risco pra JVM dizendo que uma falha ocorreu. Assim, caso o programador descuidado/esquecido (que todos nós seremos algum dia) não faça a tentativa de saque da forma certa (lembrando se deu certo ou não antes da emissão) a JVM quebra (para a aplicação e apresenta os logs) e não deixa a lógica ser corrompida.
public class TesteConta {
public static void main(String[] args) {
Conta conta = new Conta(500);
Caixa caixaEletronico = new Caixa();
try { // tenta executar a sequência com cuidado
conta.saca(700); // como não tem saldo uma exceção é lançada
// essa linha só ocorre se tudo correr bem
caixaEletronico.emite(700); // essa linha não será executada
} catch (IllegalArgumentExeption e) {
Sysout(e.getMessage()); // se houver problema o catch entra em cena
}
}
}
Ah, uma outra coisa que as exceptions ajudam é facilitar o entendimento sobre os detalhes do problema (o que o retorno do método também não consegue ajudar). Podemos lançar exceptions de vários tipos com mensagens descritivas.
Espero ter ajudado. Abraço!