Normalmente você gostaria de estruturar seus programas como:
fazalgumacoisa();
if (problema) {
return;
}
fazoutracoisa();
if (outroproblema) {
return;
}
fazaindaoutracoisa();
if (aindaoutroproblema) {
return;
}
naoseimaisoquefazer();
while (esperandoasolucao) {
fazqualquercoisa();
if (ohoh_problema) {
return;
}
}
tudobem();
O problema de fazer assim é que, embora isso seja bastante claro (é difícil deixar mais claro que isso), existem muitos lugares onde é proibido ter mais de um return dentro de uma rotina. (Bom, há gosto para tudo…)
Antigamente (no tempo que “goto” era só um sobrenome japonês, e você podia usar e abusar à vontade do GOTO) você podia escrever assim:
fazalgumacoisa();
if (problema) {
goto trataerro;
}
fazoutracoisa();
if (outroproblema) {
goto trataerro;
}
fazaindaoutracoisa();
if (aindaoutroproblema) {
goto trataerro;
}
naoseimaisoquefazer();
while (esperandoasolucao) {
fazqualquercoisa();
if (ohoh_problema) {
goto trataerro;
}
}
tudobem();
return;
trataerro:
tratamentodeerros();
Como goto não funciona no Java, então inventaram a lógica invertida, que é um “if” dentro do outro.
Eu chamo isso de “Programação 45 graus” ou “Programação em serra” porque você vê claramente triângulos ou “dentes de serra” no seu código.
Obviamente, como tudo que é invertido, é difícil de entender.
E é por isso que a complexidade ciclométrica (uma medida que aqueles engenheiros de software inventaram para te deixar com mais cabelos brancos, e basicamente mede quantos ifs e whiles você tem dentro de sua rotina) vai lá para cima.
fazalgumacoisa();
if (!problema) {
fazoutracoisa();
if (!outroproblema) {
fazaindaoutracoisa();
if (!aindaoutroproblema) {
naoseimaisoquefazer();
while (esperandoasolucao) {
fazqualquercoisa();
if (ohoh_problema) {
break;
}
}
if (!ohoh_problema) {
tudobem();
}
}
}
}
Goto não existe no Java, mas existem labels e o comando “break”. A única coisa invertida que
há é que o label é posto NO COMEÇO DO LOOP, não no fim do loop, que seria para onde o “break” iria.
O programa original ficaria como:
bloco1: do {
fazalgumacoisa();
if (problema) {
break bloco1;
}
fazoutracoisa();
if (outroproblema) {
break bloco1;
}
fazaindaoutracoisa();
if (aindaoutroproblema) {
break bloco1;
}
naoseimaisoquefazer();
while (esperandoasolucao) {
fazqualquercoisa();
if (ohoh_problema) {
break bloco1;
}
}
tudobem();
} while (false);
A complexidade ciclométrica cai (para o PMD ou Checkstyle, não sei qual dos pacotes, isso é apenas um if dentro de um loop), e é bastante fácil entender isso se explicar o que é “break label;” e “do { } while (false);” que são dois truques da linguagem.
Só não sei se o PMD ou o Checkstyle vão chiar com o “break label;” que realmente não é muito usado.
P.S. Ora, tudo que falei daria para resolver fazendo um monte de “throw new XXXException” e “catch (XXXException ex)”. Mas isso nem sempre é desejável (por problemas de desempenho, e porque Exceptions são condições excepcionais, não formas de modificar o fluxo de seu programa.)
P.S. 2 - Acho que me expressei mal - é que em muitos lugares (fábricas de software, etc.) há alguns padrões de estilo a serem seguidos; um deles é o que desestimula aos programadores o uso de múltiplos RETURNS em um mesmo método.