Property o Get Method?

Se il codice dietro un getter dovesse diventare particolarmente elaborato sarebbe più corretto spostarlo dentro un ordinario metodo? È corretto che un accessor method (cioè un metodo che, almeno idealmente, dovrebbe servire per esporre un field dello stato interno della classe al mondo esterno) faccia elaborazioni non banali, come visitare un grafo di oggetti?

Cercando una risposta a questa domanda (probabilmente futile ed accademica) possono essere fatte alcune considerazioni forse poco prevedibili.

Public field, Property, getter e setter

Qual è la differenza tra queste tre modalità di esporre un numero intero in una classe?

Con un public field

class MyClass
{
  public int Numero;
}

Con una coppia di metodi Getter/Setter

class MyClass
{
  protected int Numero;
  public int GetNumero()
  {
    return Numero;
  }
  public void SetNumero(int numero)
  {
    Numero = numero;
  }
}

Con una Property

class MyClass
{
  public int Numero { get; set; }
}

Fondamentalmente, sono tre i vantaggi nell’uso delle Property o dei getter e dei setter rispetto a quello del field pubblico:

  • la possibilità di aggiungere codice arbitrario da eseguire al momento dell’assegnazione e della lettura del valore
  • la possibilità di implementare valori a sola lettura o a sola scrittura
  • la possibilità di fare l’override dei metodi, ad esempio durante il mocking della classe

Ad esempio, mentre il field si limita ad esporre direttamente la variabile int, con gli ultimi due meccanismi è possibile realizzare facilmente un campo calcolato:

class MyClass
{
  protected string Nome;
  protected string Cognome;
  public virtual string GetNomeCompleto()
  {
    return string.Format("{0} {1}", Nome, Cognome);
  }

oppure equivalentemente

class MyClass
{
  protected string Nome;
  protected string Cognome;
  public virtual string NomeCompleto
  {
    get
    {
      return string.Format("{0} {1}", Nome, Cognome);
    }
  }

In entrambi i casi si è realizzato un campo a sola lettura.

Il mocking si realizza facilmente, anche senza apposite librerie, tramite l’overriding.

class MyMock : MyClass
{
  override public string GetNomeCompleto()
  {
    return "Mocked";
  }
}

Property o metodo?

Utilizzare le Property (supponiamo da qui in avanti di utilizzare C#, che le supporta) è considerata una buona pratica.

In alcuni casi, tuttavia, capita di domandarsi se sia preferibile ricorrere ad una Property oppure un metodo pubblico. Il dubbio può nascere qualora il codice incapsulato nella clausola get sia particolarmente articolato.

Ad esempio, supponiamo di aver modellato una fattura con una classe Fattura che conservi una collezione di voci di dettaglio, in questo modo:

class Fattura
{
  protected ICollection<Dettaglio> Dettagli;
}

class Dettaglio
{
  public int Quantita { get; set; }
  public int Sconto { get; set; }
  public double Prezzo { get; set; }
}

Supponiamo che si voglia esporre in Fattura un qualcosa che permetta di ricavare il valore (con IVA) ricavato dal totale di tutte le voci di dettaglio. Il codice per il calcolo potrebbe essere qualcosa di simile:

double totale = 0;
foreach(var dettaglio in Dettagli)
{
  totale += (dettaglio.Quantita * dettaglio.Prezzo) * (1 - dettaglio.Sconto/100);
}
return totale * (1+0.2);

Il codice potrebbe essere ancora più complesso ed includere, ad esempio, chiamate ad un ORM per il recupero dei dettagli.

La domanda è: è più corretto incapsulare il codice in una Property o in un metodo?
È più giusto, insomma, consultare quel valore con

double totale = fattura.CalcolaTotale()

o con

double totale = fattura.Totale

?

Punti di vista

La risposta più sensata, nella maggioranza dei casi, è “fa lo stesso, chi se ne frega: è una questione di lana caprina“.

Tuttavia, da quando si è passati dalla programmazione procedurale a quella ad oggetti, e da quella ad elaborare i pattern volti a disaccoppiare gli oggetti, da questi all’Inversion of Control, per arrivare alla guerra contro gli if, ai linguaggi che eliminano i punti e virgola e FAQ per placare le guerre di religione tra indentisti e parentesisti, è evidente che la programmazione ad oggetti, da mera tecnica di programmazione, sia diventata anche una filosofia di pensiero, un approccio stilistico ed estetico.

Se ci si pensa, in effetti, le Property di C# sono poco più che zucchero sintattico che risparmia agli sviluppatori C# gli inestetici getter e setter.
Per dirla con tutta onestà, anzi, si deve ammettere che, tecnicamente, l’unica differenza tra una Property e un ordinario metodo è l’impossibilità di fornire argomenti. Di fatto, sempre parlando da un punto di vista strettamente tecnico, una Property è una coppia di due metodi, uno con un solo argomento che ritorna void ed uno senza argomenti: uno può restituire qualsiasi tipo, entrambi possono disporre di implementazioni arbitrariamente complesse; la Property, come un metodo, può essere mockato, può lanciare eccezioni, può entrare in loop infiniti, può accedere al database e perfino formattare l’hard disk.

Evidentemente, il motivo che ha spinto i progettisti di linguaggi ad introdurre le Property come alternativa ai getter e setter è più filosofica ed estetica che tecnica.

Se i progettisti di C# hanno deciso per l’uso delle Property e quelli di Java no (decretando per quest’ultimo la fama di “linguaggio datato”), forse, lana caprina per lana caprina, vale comunque la pena di discuterne.

Le Property sono meglio dei getter e dei setter

In C#, l’unico contratto che è possibile stabilire nell’interazione tra oggetti è definibile tramite le interfacce.

Nei linguaggi Duck Typed come PHP è possibile dichiarare un metodo come

function RestituisciQualcosa($oggetto)
{
  return $this->qualcosaltro;
}

che, di fatto, accetta argomenti di qualsiasi tipo e restituisce tipi arbitrariamente definibili a runtime. PHP non richiede nemmeno di dichiarare quale sia il tipo restituito: lo valuta durante l’esecuzione.

Altri linguaggi sono più rigorosi e richiedono che lo sviluppatore dichiari un contratto di utilizzo del metodo: quali tipi accettare in ingresso e quale tipo restituire.

Altri linguaggi vanno perfino oltre: lo sviluppatore può raffinare il contratto dichiarando alcune constraint cui i parametri di ingresso e di uscita debbono sottostare. Ad esempio, un metodo può dichiarare di accettare solo valori interi tra 2 e 20.

Le Property servivano a questo: a garantire, se non a compile-time almeno a run-time, che un field garantisse certe constraint.

Ad esempio, per un campo rappresentante una valuta da incassare, una Property rappresentava una gran comodità per il fatto di poter essere espressa così:

double Prezzo
{
  get
  {
    return _prezzo;
  }
  set
  {
    if ( value < 0 )
      throw new WrongPriceException("Un prezzo negativo significa che devo darti soldi. Giammai");
    else
      _prezzo = value;
  }
}

La stessa cosa la si sarebbe potuta ottenere con una coppia getter/setter.
Ma la Property ha rispetto a getter e setter un grosso vantaggio: appare semanticamente come un ordinario attributo. L’utente, l’utilizzatore della classe, non percepisce che dietro a double Prezzo ci sia del codice: lo vede alla stregua di un normalissimo field pubblico.

Esteticamente ha molto più senso un

double costoTotale = articolo.Prezzo + costoDiSpedizione;

piuttosto che un inestetico

double costoTotale = articolo.GetPrezzo() + costoDiSpedizione;

Perché Prezzo deve essere un metodo? Solo perché articolo deve eseguire del codice per calcolarlo? E chi se ne frega: problemi suoi. Io volevo un “prezzo”, non un “getPrezzo()”. In qualche modo, sono legato all’implementazione della classe di articolo.

La Property risolve questo problema estetico: l’utilizzatore della classe può trattare tutti i valori esposti dalla classe come se fossero field, ignorando se abbiano o meno del codice dietro. Sono numeri, sono stringhe, sono valori: non sono “azioni da compiere”.

E, soprattutto, possono essere trattati uniformemente, che vadano scritti o che vadano letti.
Mai più

if(articolo.GetPrezzo() < 100)
{
  // aumenta il ricarico
  articolo.SetPrezzo(articolo.GetPrezzo() + 20);
}

ma un più elegante, leggibile e logico

if(articolo.Prezzo < 100)
  articolo.Prezzo += 20;

Se dietro alla Property Prezzo ci sia o no del codice, è un problema completamente incapsulato dentro la classe di articolo.

Secondo Allen Holub getter e setter (gli antenati delle Property) esistono per un motivo perfino più demenziale.
Sono nati nel contesto delle librerie per gestire widget e finestre, dove i tool di UI-builder, per facilitare il proprio lavoro, avevano bisogno di ricavare, tramite reflection, alcune proprietà interne delle classi.
Per facilitare l’accesso alle proprietà interne, gli sviluppatori delle librerie riempivano le classi di metodi accessor, cioè di getter e setter, di metodi che rompevano il principio di incapsulamento per esporre al mondo esterno lo stato interno della classe.

Gli altri sviluppatori hanno iniziato a prendere la cattiva abitudine di utilizzare i metodi accessor e, peggio, di sviluppare le proprie classi imitando lo stesso design pattern.

Per evitare il diffondersi di questa cattiva abitudine, Java 1.5 prometteva di introdurre degli appositi metadata per evitare di scrivere codice che violasse clamorosamente il principio di incapsulamento come:

private int property;
public int getProperty  (         ){ return property; }
public void setProperty (int value}{ property = value; }

consentendo un più pulito

private @property int property;

Si noti che qui il field è decorato come @property ma questo non significa che sia pubblico: per il mondo esterno è a tutti gli effetti private. La libreria di UI è in grado, tramite l’introspezione, di individuare il campo indicato come @property. Ma l’implementazione resta correttamente incapsulata e rigorosamente privata.

È un concetto assai diverso dalle Property di C# che sono pensate per essere public.
Ma è noto: nel mondo Java le scelte sono così oculate (e lente) che l’ebefrenica corsa verso l’ultima novità di C#, anche se poco ragionata e dettata da mode, fa meritare al buon vecchio Java il titolo di linguaggio deprecato.

Secondo i principi più puri dell’Object Oriented, Property e accessor method dovrebbero consentire l’accesso al campo interno solo in termini di interfaccia: in nessun caso si dovrebbe consentire l’accesso diretto al campo interno. Solo in questo modo l’implementazione (in questo caso, il tipo del campo interno) può essere protetta dal codice esterno.

Problemi che probabilmente si pongono solo gli accademici e i filosofi, visto che Visual Studio nell’estrarre l’interfaccia da una classe propone un “Select all” per esporre tutto lo stato interno, con buona pace del barbuto Holub.

Le Property fanno schifo

In Smalltalk by Example: the Developers Guide Alec Sharp introduce un’incisiva prospettiva intorno alla programmazione orientata agli oggetti:

Procedural code gets information then makes decisions. Object-oriented code tells objects to do things.

Sullo stesso argomento Allen Holub sostiene:

Don’t ask for the information you need to do the work; ask the object that has the information to do the work for you

Ha un senso.

Elaborando questo principio (“Tell, Don’t Ask“) si dovrebbe concludere che, in una buona architettura Object Oriented, un oggetto dovrebbe nascondere il proprio stato interno ed esporre solo metodi che permettano ai propri utilizzatori di invocare delle azioni.

È la logica conseguenza.

È il concetto espresso da Allen Holub quando, con una certa dose di provocatorio coraggio, enuncia le proprie argomentazioni sul perché i metodi getter and setter siano Il Male (si tratta di un articolo lunghissimo e meraviglioso, letteralmente fitto di illuminanti considerazioni: un must).

I fan di Allen Holub sono più tabelani del loro idolo. In un interessante commento a “Why getter and setter methods are evil” si afferma:

A business object should [be] property free, or at least only updateable from the outside world by asking it to do something. Its internal state may be internal to the class itself […]

Insomma, se abbiamo bisogno che oggetto faccia qualcosa, ordiniamogli di farlo: in nessun caso dovremmo essere autorizzati a infilare il naso nel suo stato interno e, meno che mai, a modificarlo.

Prendiamo un oggetto definito dalla classe

class MyClass
{
  public int StatoInterno;
  public void CambiaLoStatoInterno()
  {
    this.StatoInterno = [...] // codice 
  }
}

È evidente che l’obiettivo dello sviluppatore, nel definire il metodo CambiaLoStatoInterno(), sia quello di incapsulare lo stato interno dell’oggetto e garantire che questo sia totalmente sotto il controllo della classe stessa. Gli utilizzatori esterni possono dire all’oggetto cosa fare, ed eventualmente l’oggetto valuta se cambiare il proprio stato interno.

Fatalmente, il semplice fatto di esporre il field StatoInterno permette al mondo esterno di cambiare a piacimento lo stato interno dell’oggetto, aggirando eventualmente le regole di business. Questo, a detta di alcuni, è il male.

Meglio sarebbe

class MyClass
{
  protected int StatoInterno;
  public void CambiaLoStatoInterno()
  {
    this.StatoInterno = [...] // codice 
  }
}

A che pro, allora, avere getter, setter e Property?

È chiaro che il discorso sia un po’ estremo. Il principio, tuttavia, è meritevole di una certa considerazione. Il concetto è:

L’unico modo per modificare lo stato interno di un oggetto deve essere tramite l’invocazione di azioni definite dalla logica di business

oppure

Non deve esistere modo di cambiare lo stato interno di un oggetto violando le regole di business.

Ha un senso.

Nascondere lo stato interno, evitando di pubblicare le proprietà della classe, serve anche a contenere le dipendenze: se una proprietà (un elemento dello stato interno dell’oggetto) è pubblica è esposta al rischio che venga utilizzata da altre classi; in altre parole, più proprietà pubbliche ha la classe, più è probabile che queste vengano utilizzate dal resto del mondo.
Questo significa che qualora si decidesse di modificare l’implementazione della classe cambiando il tipo di uno degli elementi del suo stato interno (operazioni che uno sviluppatore dovrebbe prendere in piena libertà, trattandosi di stato interno della classe e non di interfaccia), si rischierebbe di trovarsi in un inferno di dipendenze. Il solo cambiare il campo X da int a double, fa notare Holub, potrebbe comportare migliaia di errori di compilazione. L’aver esposto l’implementazione interna al mondo esterno, sostiene, è una leggerezza di design che può costare. E per la quale andrebbero valutati i pro e i contro in fase di design.

Il concetto è: se la classe espone un’interfaccia, con tutti i metodi che permettono di invocare azioni che eventualmente modificano lo stato interno, non c’è alcuna necessità stringente di pubblicare anche lo stato interno. Farlo espone a rischi. I getter ed i setter sono pericolosi per lo stesso motivo per cui sono pericolosi i field pubblici: espongono al codice esterno l’implementazione della classe, violando il principio di incapsulamento.
In un certo senso, siccome getter e setter violano il principio di incapsulamento, il codice che utilizzi getter e setter andrebbe considerato non object oriented. Ne’ più ne’ meno.

Commentando l’articolo “Getter and Setters are evil” su You should never use get/set functions! Is he correct? un utente caldeggia di ridimensionare l’invettiva contro i getter e i setter come accorato suggerimento a non eccedere nell’anti-pattern di pubblicare qualsiasi elemento dello stato interno mediante gettere e setter e propone un’analogia che mi sembra notevole:

A car as a good example. It exposes a well-defined, standardised high-level interface. I don’t concern myself with setSpeed(60)… is that MPH or km/h? I just accelerate, cruise, decelerate. I don’t have to think about the details in setSteeringWheelAngle(getSteeringWheelAngle()+Math.rad(-1.5)), I just turn(-1.5), and the details are taken care of under the hood.

Altri sono più possibilisti e fanno notare che le Property, proprio perché permettono di eseguire del codice al momento della lettura e della scrittura del campo, permettono di esporre selettivamente i valori dello stato interno garantendo la soddisfazione delle regole di business.

In effetti Allen Holub non è così disfattista e dichiara pacatamente:

Moreover, the presence of numerous getter and setter methods is a red flag that the program isn’t necessarily well designed from an OO perspective.

This article explains why you shouldn’t use getters and setters (and when you can use them) and suggests a design methodology that will help you break out of the getter/setter mentality

Altri ancora, molto simpaticamente, replicano che Allen Holub is evil. Il mondo della programmazione Object Oriented è bello anche per questo.

Quindi, metodo o Property?

In linea di massima, nessuno dei due.

Se si ha modo di sostituire l’accessor col quale si chiederebbe all’oggetto una sua proprietà con un metodo col quale dire all’oggetto di fare qualcosa, è preferibile.

Tell, don’t ask.

Meglio programmare in termini di “conversazione tra oggetti”, piuttosto che “indagine di valori per eseguire delle operazioni”. È il concetto che distingue la programmazione orientata agli oggetti da quella procedurale: un programma Object Oriented è il risultato della cooperazione tra oggetti che si inviano messaggi piuttosto che dell’esecuzione di procedure che consultano valori e poi prendono decisioni.

Se proprio si deve esporre un valore dello stato interno, il principio da valutare è quello dell’idempotenza (o più correttamente della nullipotenza).

L’idempotenza è un concetto derivato dalla matematica: in senso informatico si riferisce alla proprietà di un metodo di non generare alcuna differenza osservabile fra una sua invocazione e le sue invocazioni successive.

In parole povere: una funzione è idempotente (o più correttamente, nullipotente) se è totalmente priva di effetti collaterali.

Se l’invocazione di un metodo modifica lo stato interno di un oggetto, non è nullipotente.
Se si limita a fornire un valore senza alcun effetto sullo stato interno, lo è, indipendentemente dal fatto che venga o meno eseguito del codice.

Ora, la distinzione non è affatto accademica. Si tratta di domandarsi se si stia indagando sullo stato interno dell’oggetto (confidando che questo non venga modificato per il solo fatto di avervi acceduto), o se, invece, si stia chiedendo all’oggetto di eseguire un’azione, ben sapendo che l’oggetto sarà libero di provocare, come effetto collaterale, un cambiamento del suo stato interno.

Insomma: si sta dicendo all’oggetto di fare qualcosa? Allora si sa invocando un metodo. Al contrario, si sta chiedendo all’oggetto un valore? Allora si sta consultando una Property.

Nell’esempio del totale della fattura, per quanto codice esista dietro al calcolo del valore finale, si sta eseguendo senza dubbio un’operazione nullipotente. Non si tratta di dire all’oggetto di fare qualcosa: si sta chiedendo all’oggetto di esporre un suo stato interno, per quanto conservato in una collezione di altri oggetti incapsulati.

Alla domanda Difference between property and method su StackOverflow vari utenti commentano:

Methods (C# doesn’t have functions) are better for expressing things that either change the state, or which have an expectation of taking some time and not necessarily being reproducible. They don’t tend to work in binding / serialization etc.

Note that properties are actually just a special way of writing methods. There is little functional difference. It is all about expressing intent. The one thing you don’t want to expose, however, is fields (the actual intEmployeeAge instance variable).

If all you need to do is return a value, use a property.
If you need to do something before returning a value, use a function.

Properties holds object data
Functions defines object behavior

Regola del pollice

In sostanza, se nel valutare di usare un metodo o una Property ritenessi la Property lo strumento adatto, cerca di non farlo: concediti qualche minuto per rifletterci su e domandati se non ci sia il modo di rispettare il principio “Tell, don’t Ask”; domandati se l’architettura che stai allestendo sia una conversazione tra oggetti o un volgarissimo programma procedurale che utilizza oggetti (esiste un’offesa peggiore per uno sviluppatore Object Oriented del sentirsi dire “Tu scrivi codice procedurale“?), domandati mille volte se sia davvero necessario esporre un po’ dello stato interno dell’oggetto e creare, inevitabilmente, delle dipendenze.

Se invece decidessi di non aborrire le Property, una regola del pollice nel fare la scelta tra metodo e Property è quella di giocare col suo nome:

Ha senso chiamarlo doSomething? Allora, usa un metodo.
Ha senso chiamarlo getSomeValue? Allora, vai con una Property.

Alberto Brandolini ha pubblicato secoli fa un post (in inglese) sull’argomento molto arguto (e ben meno prolisso di questo).

Sayonara

2 thoughts on “Property o Get Method?

  1. La mia opinione è che le proprietà in set sono un errore (altro che smell). Si tratta di programmazione procedurale (come scrivi tu stesso).

    Le proprietà in get non necessariamente. Le proprietà in effetti devono esporre proprietà dell’oggetto, qualità che questo possiede e che possono essere utili per altri oggetti di dominio nello stesso bounded context per completare la propria elaborazione. Inoltre non c’è alcuno smell se l’esperto di dominio afferma che quel oggetto, in quel contesto, ha una certa qualità.

    La proprietà che esponi (in sola lettura) su un entità non ha nulla a che fare con il suo stato. Il fatto che sia presente nello stato stesso, o che venga calcolata in funzione di qualcos’altro è un dettaglio implementativo.

    I cambi di stato sulle entità sono gestiti attraverso CQS.

  2. Molto interessante, ma: poniamo l’esempio che un oggetto abbia un metodo deve scorrere una collezione di altri oggetti ed interrogare una loro proprietà per fare dei confronti e prendere una decisione, come farei ad applicare il “Tell, don’t Ask?” a questi oggetti della collezione?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s