A crônica é a seguinte: considere um método que retorne um elemento de uma coleção pelo índice ou nulo caso não exista o dado elemento. Um código cobaia pode ser visto a seguir.
public class Main { static String[] opcoes = {"zero", "um", "dois"}; public static void main(String[] args) { } // retornar a opção ou nulo caso não seja encontrada static String getOpcao(int numero) { // implementação aqui } }
Existem vários modos de cumprir a API do método, uma delas é introduzindo um mau cheiro no código, utilizando a captura de exceções como desvio condicional. É bem simples, veja a seguir:
public class Main { static String[] opcoes = {"zero", "um", "dois"}; public static void main(String[] args) { System.out.println(getOpcao(9)); } // retornar a opção ou nulo caso não seja encontrada static String getOpcao(int numero) { try { return opcoes[numero]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } }
Esta é uma implementação intuitiva a primeira vista, note que ela resolve o problema, ou seja, neste exemplo é impresso null, pois como não há o índice 9 o código opcoes.get(numero) lança a exceção ArrayIndexOutOfBoundsException que é capturada e suprimida, retornando null na situações excepcionais.
Existem um detalhe importante a respeito de exceções, elas introduzem um overhead, já que cada vez que acontecem, além do bloco condicional um objeto é criado para representar a exceção.
O que eu quero dizer é que esta implementação, baseada na captura da exceção, tem baixa performance se comparada com a realização de um teste, que é a refatoração proposta e inclusive documentada no livro do Fowler.
Escrevi um pequeno benchmark para instrumentar este código, veja a seguir:
public class Main { static String[] opcoes = {"zero", "um", "dois"}; public static void main(String[] args) { long inicio = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { getOpcao(9); } System.out.println((System.currentTimeMillis() - inicio) + "ms"); } // retornar a opção ou nulo caso não seja encontrada static String getOpcao(int numero) { try { return opcoes.get[numero]; } catch (ArrayIndexOutOfBoundsException e) { return null; } } }O tempo decorrido para executar 10000 (dez mil) consultas é de 110ms no meu modesto notebook equipado com um Intel Pentium Dual Core. A refatoração a seguir torna o código mais claro e também mais rápido, é a substituição de exceção por teste, veja a seguir:
public class Main { static String[] opcoes = {"zero", "um", "dois"}; public static void main(String[] args) { long inicio = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { System.out.println(getOpcao(9)); } System.out.println((System.currentTimeMillis() - inicio) + "ms"); } // retornar a opção ou nulo caso não seja encontrada static String getOpcao(int numero) { if (numero >= 0 && numero < opcoes.length) return opcoes[numero]; return null; } }
Esta implementação executa em 5ms em média, bem melhor do que os 110ms anteriores, não? A mecânica é simples, em vez de confiar no catch nós fazemos um teste para verificar a validade do parâmetro.
É isso, deixe seu comentário se quiser.
Um comentário:
Muito bom o post... E esse Matheus é fera... Abraço
Postar um comentário