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
Heading
classe
- 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
ReciprocalChanged
evento
Di questi presupposti, (3) e (4) sono probabilmente corretti (supponendo che Heading
sia 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 HeadingChanged
evento). 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 boolean
funzione 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 @reciprocal
tramite un attr_reader
o 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.
Heading
come tipo immutabile ereciprocal
restituire un nuovoHeading
è una buona pratica "pozzo del successo". (con l'avvertenza alle due chiamate areciprocal
dovrebbe restituire "la stessa cosa", cioè dovrebbero superare il test di uguaglianza.)