Non credo che ci sia una risposta agnostica al linguaggio in quanto ciò che costituisce una "proprietà" è una domanda specifica della lingua, e ciò che un chiamante di una "proprietà" si aspetta è anche una domanda specifica della lingua. Penso che il modo più fruttuoso di pensare a questo sia quello di pensare a come appare dal punto di vista del chiamante.
In C #, le proprietà sono distintive in quanto sono (convenzionalmente) maiuscole (come i metodi) ma mancano di parentesi (come le variabili di istanza pubblica). Se vedi il seguente codice, documentazione assente, cosa ti aspetti?
var reciprocalHeading = myHeading.Reciprocal;
Come un principiante in C #, ma uno che ha letto le Linee guida per l'utilizzo delle proprietà di Microsoft , mi aspetto Reciprocal, tra le altre cose:
- essere un membro di dati logici della
Headingclasse
- essere economico da chiamare, in modo tale che non sia necessario per me memorizzare nella cache il valore
- mancano effetti collaterali osservabili
- produce lo stesso risultato se chiamato due volte in successione
- (forse) offrire un
ReciprocalChangedevento
Di questi presupposti, (3) e (4) sono probabilmente corretti (supponendo che Headingsia un tipo di valore immutabile, come nella risposta di Ewan ), (1) è discutibile, (2) è sconosciuto ma anche discutibile e (5) è improbabile che ha un senso semantico (anche se qualunque cosa abbia un titolo dovrebbe forse avere un HeadingChangedevento). Questo mi suggerisce che in un'API C #, "ottenere o calcolare il reciproco" non dovrebbe essere implementato come una proprietà, ma soprattutto se il calcolo è economico eHeading è immutabile, è un caso limite.
(Si noti, tuttavia, che nessuna di queste preoccupazioni ha nulla a che fare con la chiamata alla proprietà crea una nuova istanza , nemmeno (2). La creazione di oggetti nel CLR, di per sé, è piuttosto economica.)
In Java, le proprietà sono una convenzione di denominazione dei metodi. Se vedo
Heading reciprocalHeading = myHeading.getReciprocal();
le mie aspettative sono simili a quelle sopra (se espressamente meno esplicite): mi aspetto che la chiamata sia economica, idempotente e priva di effetti collaterali. Tuttavia, al di fuori del framework JavaBeans, il concetto di "proprietà" non è poi così significativo in Java, e in particolare quando si considera una proprietà immutabile senza corrispondenti setReciprocal(), la getXXX()convenzione è ora in qualche modo antiquata. Da Effective Java , seconda edizione (ormai più di otto anni):
I metodi che restituiscono una non booleanfunzione o un attributo dell'oggetto su cui sono invocati sono generalmente denominati con un nome, una frase di nome o una frase di verbo che inizia con il verbo get…. Esiste un contingente vocale che afferma che solo la terza forma (a partire da get) è accettabile, ma ci sono poche basi per questa affermazione. Le prime due forme di solito portano a un codice più leggibile ... (p. 239)
In un'API contemporanea e più fluida, quindi, mi aspetterei di vedere
Heading reciprocalHeading = myHeading.reciprocal();
- che suggerirebbe ancora una volta che la chiamata è economica, idempotente e priva di effetti collaterali, ma non direbbe nulla sull'esecuzione di un nuovo calcolo o sulla creazione di un nuovo oggetto. Questo va bene; in una buona API, non mi dovrebbe importare.
In Ruby non esiste una proprietà. Ci sono "attributi", ma se vedo
reciprocalHeading = my_heading.reciprocal
Non ho modo immediato di sapere se sto accedendo a una variabile di istanza @reciprocaltramite un attr_readero un metodo di accesso semplice o se sto chiamando un metodo che esegue un calcolo costoso. Il fatto che il nome del metodo sia un nome semplice, invece di direcalcReciprocal , suggerisce, ancora una volta, che la chiamata è almeno economica e probabilmente non ha effetti collaterali.
In Scala, la convenzione di denominazione è che i metodi con effetti collaterali prendono parentesi e metodi senza di loro no, ma
val reciprocal = heading.reciprocal
potrebbe essere uno dei seguenti:
// 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 = …
(Nota che Scala consente varie cose che sono comunque scoraggiate dalla guida di stile . Questo è uno dei miei principali fastidi con Scala.)
La mancanza di parentesi mi dice che la chiamata non ha effetti collaterali; il nome, ancora una volta, suggerisce che la chiamata dovrebbe essere relativamente economica. Oltre a ciò, non mi interessa come mi dia il valore.
In breve: Conosci la lingua che stai usando e conosci quali aspettative altri programmatori porteranno alla tua API. Tutto il resto è un dettaglio di implementazione.
Headingcome tipo immutabile ereciprocalrestituire un nuovoHeadingè una buona pratica "pozzo del successo". (con l'avvertenza alle due chiamate areciprocaldovrebbe restituire "la stessa cosa", cioè dovrebbero superare il test di uguaglianza.)