Quando estou tentando criar uma interface para um programa específico, geralmente estou tentando evitar exceções que dependem de entradas não validadas.
Então, o que geralmente acontece é que eu pensei em um pedaço de código como este (este é apenas um exemplo para o exemplo, não se importe com a função que ele executa, por exemplo, em Java):
public static String padToEvenOriginal(int evenSize, String string) {
if (evenSize % 2 == 1) {
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, digamos que evenSize
seja realmente derivado da entrada do usuário. Então, eu não tenho certeza se é mesmo. Mas não quero chamar esse método com a possibilidade de uma exceção ser lançada. Então, eu faço a seguinte função:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
mas agora eu tenho duas verificações que executam a mesma validação de entrada: a expressão na if
instrução e a verificação explícita isEven
. Código duplicado, nada legal, então vamos refatorar:
public static String padToEvenWithIsEven(int evenSize, String string) {
if (!isEven(evenSize)) { // to avoid duplicate code
throw new IllegalArgumentException("evenSize argument is not even");
}
if (string.length() >= evenSize) {
return string;
}
StringBuilder sb = new StringBuilder(evenSize);
sb.append(string);
for (int i = string.length(); i < evenSize; i++) {
sb.append(' ');
}
return sb.toString();
}
OK, isso resolveu, mas agora entramos na seguinte situação:
String test = "123";
int size;
do {
size = getSizeFromInput();
} while (!isEven(size)); // checks if it is even
String evenTest = padToEvenWithIsEven(size, test);
System.out.println(evenTest); // checks if it is even (redundant)
agora temos uma verificação redundante: já sabemos que o valor é par, mas padToEvenWithIsEven
ainda realiza a verificação do parâmetro, que sempre retornará verdadeiro, como já chamamos essa função.
Agora isEven
, é claro, não representa um problema, mas se a verificação de parâmetros for mais complicada, isso poderá resultar em um custo muito alto. Além disso, realizar uma chamada redundante simplesmente não parece certo.
Às vezes, podemos contornar isso introduzindo um "tipo validado" ou criando uma função onde esse problema não pode ocorrer:
public static String padToEvenSmarter(int numberOfBigrams, String string) {
int size = numberOfBigrams * 2;
if (string.length() >= size) {
return string;
}
StringBuilder sb = new StringBuilder(size);
sb.append(string);
for (int i = string.length(); i < size; i++) {
sb.append('x');
}
return sb.toString();
}
mas isso requer um pensamento inteligente e um refator bastante grande.
Existe uma maneira (mais) genérica pela qual podemos evitar chamadas redundantes isEven
e executar a verificação de duplo parâmetro? Eu gostaria que a solução não chamasse padToEven
com um parâmetro inválido, acionando a exceção.
Sem exceções, não quero dizer programação livre de exceções, quero dizer que a entrada do usuário não aciona uma exceção por design, enquanto a própria função genérica ainda contém a verificação de parâmetros (apenas para proteger contra erros de programação).
padToEvenWithIsEven
não executa a validação da entrada do usuário. Ele executa uma verificação de validade em sua entrada para se proteger contra erros de programação no código de chamada. A extensão dessa validação depende de uma análise de custo / risco em que você coloca o custo da verificação no risco de que a pessoa que está escrevendo o código de chamada passe no parâmetro errado.