Je ne pense pas qu'il y ait une réponse indépendante de la langue à cela, car ce qui constitue une «propriété» est une question spécifique à la langue, et ce qu'un appelant d'une «propriété» attend est également une question spécifique à la langue. Je pense que la façon la plus fructueuse d'y penser est de penser à quoi cela ressemble du point de vue de l'appelant.
En C #, les propriétés se distinguent en ce qu'elles sont (conventionnellement) capitalisées (comme les méthodes) mais manquent de parenthèses (comme les variables d'instances publiques). Si vous voyez le code suivant, sans documentation, à quoi vous attendez-vous?
var reciprocalHeading = myHeading.Reciprocal;
En tant que novice en C #, mais qui a lu les directives d'utilisation des propriétés de Microsoft , je m'attendrais Reciprocal
à, entre autres:
- être un membre logique des données de la
Heading
classe
- être peu coûteux à appeler, de sorte que je n'ai pas besoin de mettre en cache la valeur
- manque d'effets secondaires observables
- produire le même résultat si appelé deux fois de suite
- (peut-être) offrir un
ReciprocalChanged
événement
Parmi ces hypothèses, (3) et (4) sont probablement correctes (en supposant qu'il Heading
s'agit d'un type de valeur immuable, comme dans la réponse d'Ewan ), (1) est discutable, (2) est inconnu mais également discutable, et (5) est peu susceptible de avoir un sens sémantique (bien que tout ce qui a un titre devrait peut-être avoir un HeadingChanged
événement). Cela me suggère que dans une API C #, «obtenir ou calculer l'inverse» ne devrait pas être implémenté en tant que propriété, mais surtout si le calcul est bon marché et Heading
immuable, c'est un cas limite.
(Notez, cependant, qu'aucune de ces préoccupations n'a quoi que ce soit à voir avec le fait d'appeler la propriété crée une nouvelle instance , pas même (2). La création d'objets dans le CLR, en soi, est assez bon marché.)
En Java, les propriétés sont une convention de dénomination de méthode. Si je vois
Heading reciprocalHeading = myHeading.getReciprocal();
mes attentes sont similaires à celles ci-dessus (si elles sont moins explicites): je m'attends à ce que l'appel soit bon marché, idempotent et dépourvu d'effets secondaires. Cependant, en dehors du cadre JavaBeans, le concept de «propriété» n'a pas beaucoup de sens en Java, et en particulier lorsque l'on considère une propriété immuable sans correspondant setReciprocal()
, la getXXX()
convention est maintenant quelque peu démodée. De Effective Java , deuxième édition (déjà plus de huit ans maintenant):
Les méthodes qui renvoient une non- boolean
fonction ou un attribut de l'objet sur lequel elles sont invoquées sont généralement nommées avec un nom, une phrase nominale ou une expression verbale commençant par le verbe get
…. Il existe un contingent vocal qui prétend que seule la troisième forme (en commençant par get
) est acceptable, mais cette affirmation est peu fondée. Les deux premiers formulaires conduisent généralement à un code plus lisible… (p. 239)
Dans une API contemporaine plus fluide, je m'attendrais donc à voir
Heading reciprocalHeading = myHeading.reciprocal();
- ce qui suggérerait à nouveau que l'appel est bon marché, idempotent et manque d'effets secondaires, mais ne dirait rien si un nouveau calcul est effectué ou si un nouvel objet est créé. C'est bon; dans une bonne API, je m'en fiche.
En Ruby, une propriété n'existe pas. Il y a des «attributs», mais si je vois
reciprocalHeading = my_heading.reciprocal
Je n'ai aucun moyen immédiat de savoir si j'accède à une variable d'instance @reciprocal
via une attr_reader
ou une méthode d'accesseur simple, ou si j'appelle une méthode qui effectue un calcul coûteux. Le fait que le nom de la méthode soit un simple nom, bien que, plutôt que de dire calcReciprocal
, suggère, encore une fois, que l'appel est au moins bon marché et n'a probablement pas d'effets secondaires.
Dans Scala, la convention de dénomination est que les méthodes avec effets secondaires prennent des parenthèses et les méthodes sans elles ne le font pas, mais
val reciprocal = heading.reciprocal
pourrait être:
// immutable public value initialized at creation time
val reciprocal: Heading = …
// immutable public value initialized on first use
lazy val reciprocal: Heading = …
// public method, probably recalculating on each invocation
def reciprocal: Heading = …
// as above, with parentheses that, by convention, the caller
// should only omit if they know the method has no side effects
def reciprocal(): Heading = …
(Notez que Scala autorise diverses choses qui sont néanmoins découragées par le guide de style . C'est l'une de mes principales contrariétés avec Scala.)
Le manque de parenthèses me dit que l'appel n'a pas d'effets secondaires; le nom, encore une fois, suggère que l'appel devrait être relativement bon marché. Au-delà de cela, je me fiche de savoir comment cela me donne de la valeur.
En bref: Connaissez la langue que vous utilisez et sachez quelles attentes les autres programmeurs apporteront à votre API. Tout le reste est un détail d'implémentation.
Heading
comme type immuable etreciprocal
renvoyer un nouveauHeading
est une bonne pratique "fosse de réussite". (avec la mise en garde lors des deux appels àreciprocal
devrait retourner "la même chose", c'est-à-dire qu'ils devraient passer le test d'égalité.)