Lorsque j'essaie de créer une interface pour un programme spécifique, j'essaie généralement d'éviter de lever des exceptions qui dépendent d'une entrée non validée.
Donc, ce qui se produit souvent, c'est que j'ai pensé à un morceau de code comme celui-ci (ce n'est qu'un exemple pour un exemple, ne me dérange pas la fonction qu'il exécute, exemple en 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, alors disons que cela evenSize
est dérivé de l'entrée utilisateur. Je ne suis donc pas sûr que ce soit égal. Mais je ne veux pas appeler cette méthode avec la possibilité qu'une exception soit levée. Je fais donc la fonction suivante:
public static boolean isEven(int evenSize) {
return evenSize % 2 == 0;
}
mais maintenant j'ai deux contrôles qui effectuent la même validation d'entrée: l'expression dans l' if
instruction et le contrôle explicite isEven
. Duplicata, pas sympa, alors refactorisons:
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, cela l'a résolu, mais maintenant nous entrons dans la situation suivante:
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)
nous avons maintenant une vérification redondante: nous savons déjà que la valeur est paire, mais nous effectuons padToEvenWithIsEven
toujours la vérification des paramètres, qui retournera toujours vrai, comme nous l'avons déjà appelé cette fonction.
Maintenant, isEven
bien sûr, cela ne pose pas de problème, mais si la vérification des paramètres est plus lourde, cela peut entraîner des coûts trop élevés. En plus de cela, effectuer un appel redondant ne se sent tout simplement pas bien.
Parfois, nous pouvons contourner cela en introduisant un "type validé" ou en créant une fonction où ce problème ne peut pas se produire:
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();
}
mais cela nécessite une réflexion intelligente et un grand refactor.
Existe-t-il un moyen (plus) générique pour éviter les appels redondants isEven
et effectuer une double vérification des paramètres? J'aimerais que la solution ne fasse pas appel padToEven
avec un paramètre non valide, déclenchant l'exception.
Sans aucune exception, je ne veux pas dire une programmation sans exception, je veux dire que l'entrée utilisateur ne déclenche pas une exception par conception, tandis que la fonction générique elle-même contient toujours la vérification des paramètres (ne serait-ce que pour se protéger contre les erreurs de programmation).
padToEvenWithIsEven
n'est pas effectuée. Il effectue un contrôle de validité sur son entrée pour se protéger contre les erreurs de programmation dans le code appelant. L'étendue de cette validation dépend d'une analyse coût / risque dans laquelle vous comparez le coût de la vérification au risque que la personne qui écrit le code appelant passe le mauvais paramètre.