Supposons que nous ayons une méthode foo(String bar)
qui ne fonctionne que sur des chaînes qui répondent à certains critères; par exemple, il doit être en minuscules, ne doit pas être vide ou ne comporter que des espaces, et doit correspondre au modèle [a-z0-9-_./@]+
. La documentation de la méthode énonce ces critères.
La méthode devrait-elle rejeter tout écart par rapport à ces critères, ou la méthode devrait-elle être plus indulgente à l'égard de certains critères? Par exemple, si la méthode initiale est
public void foo(String bar) {
if (bar == null) {
throw new IllegalArgumentException("bar must not be null");
}
if (!bar.matches(BAR_PATTERN_STRING)) {
throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
}
this.bar = bar;
}
Et la deuxième méthode de pardon est
public void foo(String bar) {
if (bar == null) {
throw new IllegalArgumentException("bar must not be null");
}
if (!bar.matches(BAR_PATTERN_STRING)) {
bar = bar.toLowerCase().trim().replaceAll(" ", "_");
if (!bar.matches(BAR_PATTERN_STRING) {
throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
}
}
this.bar = bar;
}
La documentation doit-elle être modifiée pour indiquer qu'elle sera transformée et définie si possible sur la valeur transformée, ou la méthode doit-elle être aussi simple que possible et rejeter toutes les déviations? Dans ce cas, bar
pourrait être défini par l'utilisateur d'une application.
Le cas d'utilisation principal serait que les utilisateurs accèdent aux objets à partir d'un référentiel par un identificateur de chaîne spécifique. Chaque objet du référentiel doit avoir une chaîne unique pour l'identifier. Ces référentiels pouvaient stocker les objets de différentes manières (serveur sql, json, xml, binaire, etc.) et j'ai donc essayé d'identifier le plus petit dénominateur commun qui correspondrait à la plupart des conventions de dénomination.
la source
foo
fonction stricte qui est rigoureuse dans les arguments qu'il accepte, et avoir une deuxième fonction d'aide qui peut essayer de "nettoyer" un argument à utiliserfoo
. De cette façon, chaque méthode a moins à faire seule et peut être gérée et intégrée plus proprement. Si vous vous engagez dans cette voie, il serait probablement également utile de vous éloigner d'une conception exceptionnellement lourde; vous pouvez utiliser quelque chose comme à laOptional
place, puis avoir les fonctions qui consommentfoo
des exceptions de levée si nécessaire.Réponses:
Votre méthode doit faire ce qu'elle dit.
Cela empêche les bogues, à la fois de l'utilisation et de la modification ultérieure du comportement des responsables. Cela fait gagner du temps car les responsables n'ont pas besoin de passer autant de temps à comprendre ce qui se passe.
Cela dit, si la logique définie n'est pas conviviale, elle devrait peut-être être améliorée.
la source
foo
etfooForUncleanString
méthodes où ce dernier fait les corrections avant de le passer au premier.)Il y a quelques points:
N'oubliez pas qu'il existe des assertions de débogage pour diagnostiquer les erreurs logiques en mode débogage, ce qui atténue principalement les problèmes de performances.
Si vous implémentez une interface utilisateur, des messages d'erreur conviviaux (y compris des suggestions et d'autres aides) font partie d'une bonne conception.
Mais rappelez-vous que les API sont destinées aux programmeurs et non aux utilisateurs finaux.
Une expérience réelle d'être flou et permissif avec une entrée est le HTML.
Ce qui a amené tout le monde à le faire différemment, et la spécification, maintenant documentée, est un gigantesque tome plein de cas spéciaux.
Voir la loi de Postel (" Soyez conservateur dans ce que vous faites, soyez libéral dans ce que vous acceptez des autres. ") Et un critique qui en parle ( Ou bien mieux que MichaelT m'a fait connaître ).
la source
Le comportement d'une méthode doit être clair, intuitif, prévisible et simple. En général, nous devrions être très réticents à effectuer un traitement supplémentaire sur l'entrée d'un appelant. De telles suppositions sur ce que l'appelant voulait invariablement ont beaucoup de cas marginaux qui produisent un comportement indésirable. Considérez une opération aussi simple que la jonction d'un chemin de fichier. De nombreuses (ou peut-être même la plupart) des fonctions de jonction de chemin de fichier élimineront silencieusement tous les chemins précédents si l'un des chemins en cours de jonction semble être enraciné! Par exemple,
/abc/xyz
joint à/evil
entraînera juste/evil
. Ce n'est presque jamais ce que j'ai l'intention de joindre aux chemins de fichiers, mais comme il n'y a pas d'interface qui ne se comporte pas de cette façon, je suis obligé d'avoir des bogues ou d'écrire du code supplémentaire couvrant ces cas.Cela dit, il existe de rares occasions où il est logique qu'une méthode soit "indulgente", mais il devrait toujours être du ressort de l'appelant de décider quand et si ces étapes de traitement s'appliquent à sa situation. Ainsi, lorsque vous avez identifié une étape de prétraitement commune que vous souhaitez appliquer aux arguments dans diverses situations, vous devez exposer les interfaces pour:
Le dernier est facultatif; vous ne devez le fournir que si un grand nombre d'appels l'utilisent.
L'exposition de la fonctionnalité brute donne à l'appelant la possibilité de l'utiliser sans l'étape de prétraitement quand il en a besoin. L'exposition de l'étape du préprocesseur par elle-même permet à l'appelant de l'utiliser dans des situations où il n'appelle même pas la fonction ou lorsqu'il souhaite prétraiter une entrée avant d' appeler la fonction (comme lorsqu'il souhaite d'abord la passer dans une autre fonction). Fournir la combinaison permet aux appelants d'invoquer les deux sans tracas, ce qui est utile principalement si la plupart des appelants l'utilisent de cette façon.
la source
Comme d'autres l'ont dit, rendre la correspondance de chaîne «indulgente» signifie introduire une complexité supplémentaire. Cela signifie plus de travail dans la mise en œuvre de l'appariement. Vous avez maintenant beaucoup plus de cas de test, par exemple. Vous devez effectuer un travail supplémentaire pour vous assurer qu'il n'y a pas de noms sémantiquement égaux dans l'espace de noms. Plus de complexité signifie également qu'il y a plus à se tromper à l'avenir. Un mécanisme plus simple, comme un vélo, nécessite moins d'entretien qu'un mécanisme plus complexe, comme une voiture.
La correspondance de chaîne indulgente vaut-elle donc tout ce coût supplémentaire? Cela dépend du cas d'utilisation, comme d'autres l'ont noté. Si les chaînes sont une sorte d'entrée externe vous ne contrôlez pas, et il y a un avantage certain à la correspondance clémente, il pourrait valoir la peine. Peut-être que les informations proviennent d'utilisateurs finaux qui ne sont peut-être pas très consciencieux sur les caractères de l'espace et la capitalisation, et vous êtes fortement incité à rendre votre produit plus facile à utiliser.
D'un autre côté, si l'entrée provenait, disons, de fichiers de propriétés assemblés par des techniciens, qui devraient comprendre cela
"Fred Mertz" != "FredMertz"
, je serais plus enclin à rendre la correspondance plus stricte et à économiser les coûts de développement.Je pense que dans tous les cas, il est utile de rogner et d'ignorer les espaces de début et de fin, cependant - j'ai vu trop d'heures gaspillées à déboguer ce genre de problèmes.
la source
Vous mentionnez une partie du contexte d'où vient cette question.
Étant donné que, je voudrais que la méthode fasse juste une chose, elle affirme les exigences sur la chaîne, laissez-la s'exécuter en fonction de cela - je n'essaierais pas de la transformer ici. Restez simple et clair; documentez- le et essayez de synchroniser la documentation et le code.
Si vous souhaitez transformer les données provenant de la base de données utilisateur d'une manière plus indulgente, mettez cette fonctionnalité dans une méthode de transformation distincte et documentez la fonctionnalité qui la lie .
À un moment donné, les exigences de la fonction doivent être mesurées, clairement documentées et l'exécution doit se poursuivre. Le «pardon», à son sens, est un peu muet, c'est une décision de conception et je dirais que la fonction ne mute pas son argument. Le fait que la fonction mute l'entrée masque une partie de la validation qui serait requise du client. Avoir une fonction qui fait la mutation aide le client à bien faire les choses.
La grande importance ici est la clarté et la documentation de ce que fait le code .
la source
la source