Em muitos programas, especialmente nos primeiros contatos com orientação a objetos, é comum surgir a dúvida: onde devem ficar as regras de validação? Na aplicação? Dentro da classe de domínio? Neste post, você vai entender, usando um exemplo prático, como o tratamento de exceções com try e catch em Java ajuda a organizar responsabilidades, tornar o código mais claro e reforçar os princípios de encapsulamento, coesão, e delegação na construção de sistemas.
O problema: cálculo de financiamento com regras de negócio
Os dados de um financiamento são:
- Valor total do financiamento.
- Valor pago na entrada.
- Número de parcelas.
Deseja-se fazer um programa que, informados os dados de um financiamento, mostre quanto é o valor de cada prestação do financiamento. O valor da prestação é o valor total menos o valor de entrada, dividido pelo número de parcelas.
Exemplo:
Entrada:
- Valor total = 1000
- Valor da entrada = 300
- Quantidade de prestações = 10
Saída = 70 (pois descontando 300 de entrada ao valor total de 1000, sobra 700, que dividido em 10 prestações resulta em parcelas de 70 cada)
O programa ainda deve verificar as seguintes regras, informando uma mensagem de erro caso alguma regra seja violada:
- O valor da entrada deve ser pelo menos 20% do valor total do financiamento.
- O número mínimo de parcelas deve ser 6.
Aqui surge uma pergunta importante: onde essas validações devem ficar? Na aplicação? Ou dentro do próprio objeto de domínio?
A solução ruim: validação espalhada na aplicação
Primeiro, vejamos uma implementação possível.
public class Financiamento { public Double valorTotal; public Double entrada; public Integer parcelas; public Financiamento(double valorTotal, double entrada, int parcelas) { this.valorTotal = valorTotal; this.entrada = entrada; this.parcelas = parcelas; } public double prestacao() { return (valorTotal - entrada) / parcelas; } }
public class ProgramaPrincipal { public static void main(String[] args) { double valorTotal = 1000.0; double entrada = 300.0; int parcelas = 10; if (entrada < valorTotal * 0.2) { System.out.println("A entrada deve ser pelo menos 20% do valor total"); } else if (parcelas < 6) { System.out.println("O número mínimo de parcelas deve ser 6"); } else { Financiamento f = new Financiamento(valorTotal, entrada, parcelas); System.out.println(f.prestacao()); } } }
Por que essa solução é ruim?
Existem alguns problemas importantes aqui:
- A lógica de validação está na aplicação (ProgramaPrincipal) e não no domínio (Financiamento).
- O objeto Financiamento pode ser criado em estado inválido se alguém esquecer de validar.
- O uso de if-else encadeados não é a forma mais clara e escalável de expressar regras de negócio.
- A responsabilidade está espalhada: parte da regra está fora da classe que representa o conceito.
Em orientação a objetos, o objeto deve ser responsável por manter sua própria consistência. Se um financiamento não pode existir com entrada menor que 20% ou com menos de 6 parcelas, então essa regra pertence ao próprio financiamento.
A solução boa: validação no domínio com exceções
Agora veja uma abordagem mais adequada.
public class Financiamento { public Double valorTotal; public Double entrada; public Integer parcelas; public Financiamento(double valorTotal, double entrada, int parcelas) { if (entrada < valorTotal * 0.2) { throw new RuntimeException("A entrada deve ser pelo menos 20% do valor total"); } if (parcelas < 6) { throw new RuntimeException("O número mínimo de parcelas deve ser 6"); } this.valorTotal = valorTotal; this.entrada = entrada; this.parcelas = parcelas; } public double prestacao() { return (valorTotal - entrada) / parcelas; } }
public class ProgramaPrincipal { public static void main(String[] args) { double valorTotal = 1000.0; double entrada = 300.0; int parcelas = 10; try { Financiamento f = new Financiamento(valorTotal, entrada, parcelas); System.out.println(f.prestacao()); } catch (RuntimeException e) { System.out.println(e.getMessage()); } } }
Por que essa solução é melhor?
Agora temos melhorias importantes:
- O próprio objeto Financiamento garante sua consistência.
- Não é possível criar um financiamento inválido.
- A aplicação apenas tenta usar o objeto e trata possíveis erros.
- As regras de negócio estão centralizadas no domínio.
Essa é uma aplicação prática de encapsulamento: o objeto protege seu próprio estado.
Entendendo o papel do try e catch
Quando usamos throw, estamos interrompendo o fluxo normal do programa e sinalizando que algo deu errado.
O bloco try indica que estamos executando um trecho de código que pode gerar uma exceção.
O bloco catch captura essa exceção e permite que tratemos o erro de forma controlada.
No nosso exemplo:
- Se os dados forem válidos, o financiamento é criado e a prestação é exibida.
- Se alguma regra for violada, a exceção é lançada.
- O
catchintercepta a exceção e exibe a mensagem apropriada.
Esse mecanismo separa claramente:
- Regra de negócio (domínio)
- Tratamento de erro (camada de aplicação)
Assista a aula em vídeo com o prof. Dr. Nelio Alves no Youtube:
Conclusão: exceções como ferramenta de modelagem
Tratamento de exceções em Java não serve apenas para evitar que o programa "quebre". Ele é uma ferramenta de modelagem.
Ao lançar exceções dentro do domínio, estamos dizendo:
"Este objeto não pode existir nesse estado."
Isso torna o código:
- Mais seguro
- Mais organizado
- Mais alinhado com os princípios de orientação a objetos
- Mais fácil de evoluir
Entender try, catch e throw é entender como proteger a integridade do seu modelo de negócio. E essa é uma das diferenças fundamentais entre código que apenas funciona e código bem projetado.

