Quelle est la différence entre "to" et "as" préfixes de nom de méthode comme
Quand utiliser lequel lors de la conception d'une méthode?
Une fonction toXYZ()
est censée effectuer une conversion et renvoyer un nouvel objet indépendant (bien que l'immuabilité permette l'optimisation, Java.lang.String.toString()
renvoie juste l'objet).
Par exemple, en C++, nous avons std::bitset::to_ulong()
qui peut facilement échouer, et toute une pléthore de to_string()
, tous faisant un (plus ou moins) conversion complexe et allocation de mémoire.
En revanche, une fonction asXYZ()
devrait renvoyer une vue (potentiellement différente) de la source, en effectuant un travail minimal.
Par exemple, en C++, nous avons std::as_const()
qui renvoie juste une référence constante, et le plus impliqué std::forward_as_Tuple
qui fait également référence à ses arguments par référence.
Honnêtement, il s'agit peut-être simplement d'incohérence de dénomination. Si vous regardez des objets de bibliothèque standard dans Smalltalk, par exemple, toutes les méthodes qui font des "conversions complexes" ou "renvoient des représentations simples dans un autre type" sont préfixées avec as
, comme dans asString
, asFloat
, asSeconds
, et la méthode standard pour convertir n'importe quoi en autre chose, as: aClass
.
Dans Ruby, les mêmes types de méthodes sont précédés de to_
, un péché to_s
, to_a
, to_h
, abréviation de string
, array
et hash
respectivement.
Aucune des bibliothèques standard ne semble faire la différence entre différents types de conversions, probablement parce qu'elle doit être considérée comme un détail d'implémentation.
Cependant, en Java nous voyons beaucoup de confusion. Comme vous l'avez mentionné, il y a toString
, asList
, et ainsi de suite. Je crois que ce ne sont que une incohérence de dénomination, car si vous essayez de définir une signification différente pour chaque préfixe, vous trouverez toujours un contre-exemple ailleurs dans la bibliothèque standard.
Donc, dans tous les cas, je dirais que l'important est que vous et votre équipe choisissiez un préfixe et que vous l'utilisiez de manière cohérente dans tout le code. La cohérence est la clé, donc les gens ne sont pas laissés se demander, comme vous l'avez dû.
Bien qu'il existe déjà une réponse acceptée, elle semble se concentrer sur C++, tandis que la question est étiquetée avec Java. En Java, le premier exemple qui vient à l'esprit pour ce genre de chose est Arrays.asList, qui retourne, essentiellement, une vue d'un tableau, enveloppé dans une liste. Le tableau sous-jacent et la liste sont toujours connectés; les modifications apportées au tableau sont reflétées dans la liste et vice versa. Cependant, le tableau renvoyé par la méthode toArray de la liste est indépendant du tableau d'origine et de la liste:
String[] wordArray = {"one", "fine", "day"};
List<String> wordList = Arrays.asList(wordArray);
// changes to the array are visible in the list
System.out.println(wordList); // prints "[one, fine, day]"
wordArray[1] = "horrible";
System.out.println(wordList); // prints "[one, horrible, day]"
// changes to the list are visible in the array
wordList.set(1, "beautiful");
System.out.println(wordArray[1]); // prints "beautiful"
// but changes to the list or array don't affect the
// result from the list's toArray method.
String[] moreWords = wordList.toArray(new String[] {});
wordList.set(0, "the");
wordArray[1] = "best";
for (int i=0; i<3; i++) {
System.out.println(moreWords[i]); // prints "one", "beautiful", and "day"
}
Cela dit, il n'y a aucune garantie que chaque développeur de bibliothèque respecte cette convention, vous devez donc vérifier la documentation pour savoir si c'est le comportement que vous obtiendrez avec du code inconnu.
L'autre endroit que j'ai vu comme ... () les méthodes utilisées fréquemment est la descente des types en sous-types. Par exemple, si vous avez un ensemble énuméré de sous-types, vous pourriez vous retrouver avec du code comme:
/**
* Every Node is either an ANode or a BNode.
*/
interface Node {
/**
* Returns this Node as an ANode.
*
* @return this node
*/
default ANode asANode() {
if (this instanceof ANode) {
return (ANode) this;
}
else {
throw new UnsupportedOperationException();
}
// Or, in Java8 style, perhaps:
// return Optional.of(this)
// .filter(ANode.class::isInstance)
// .map(ANode.class::cast)
// .orElseThrow(UnsupportedOperationException::new);
}
/**
* Returns this Node as a BNode.
*
* @return this node
*/
default BNode asBNode() {
if (this instanceof BNode) {
return (BNode) this;
}
else {
throw new UnsupportedOperationException();
}
}
}
La différence que j'ai remarquée (tout à l'heure en y réfléchissant) est
Nous voyons donc AsInteger et AsString et nous voyons ToArray et ToStringList.
Cela implique une conversion qui a du sens (c'est un mouvement, un processus). Comme implique une représentation, une manière d'exprimer l'objet originel.
Une autre façon de voir les choses:
Et puis il y a "l'art antérieur" (ou l'héritage) à traiter. Avant que les langues ne soient complètement OO à partir de zéro, vous auriez des fonctions de bibliothèque comme StrToInt () et IntToStr (). Après tout, To est plus actif que As. Je pense particulièrement à Delphi ici.
Lorsque C # a été conçu avec l'ambition d'aller OO jusqu'au bout, il était logique d'avoir une méthode sur l'objet désormais entier qui convertirait l'entier en chaîne. Bien que nous ayons également un Convert classe, la conversion en chaîne est si courante qu'elle a été transformée en méthode virtuelle sur un objet. Les concepteurs ont peut-être pensé que ToString serait plus familier aux personnes de l'ancien paradigme et c'est peut-être pourquoi nous avons obtenu une méthode virtuelle ToString () et non une propriété virtuelle AsString.