martedì 30 giugno 2009

Setter, Getter o entrambi?

I Java Beans hanno introdotto diverse convenzioni, fra le quali quelle circa l'utilizzo dei metodi getter e setter: i primi sono metodi che ritornano il valore di un attributo d'istanza, i secondi sono mutator che impostano il valore di tale attributo. La convenzione vuole che il getter abbia un nome che inizia con get, seguito dal nome della proprietà (variabile di istanza) con la prima lettera maiuscola e non abbia alcun parametro, ma ritorni un tipo pari a quello della proprietà. Il setter segue regole simili, e ha il suo nome composto da set e il nome della proprietà (prima lettera maiuscola) e riceva esattamente un argomento del tipo della proprietà senza ritornare al chiamante nulla.
Da quanto sopra si ha che per ogni proprietà servono almeno due metodi distinti, un getter e un setter, abbastanza semplici e rapidi da scrivere (addirittura gli IDE moderni consentono di creare tali metodi con un click). E' però possibile prendere in prestito da Perl il comportamento dei metodi e scrivere un metodo unico che svolga entrambe le funzioni.
Si consideri la seguente semplice classe:

public class Person {

private String name = null;
private String surname = null;
...
}



e si supponga di voler scrivere un unico metodo name che possa funzionare sia da getter che da setter a seconda del contesto in cui viene chiamato. In altre parole il seguente main deve funzionare:

public static void main(String[] args) {
Person p = new Person();
p.name( "Luca" );
System.out.println("Persona " + p.name() );

}

Come si può notare il metodo name viene chiamato in due contesti diversi: se gli viene passato un parametro si deve comportare come setter, altrimenti come getter.
Come si puo' ottenere un simile comportamento? Usando gli argomenti opzionali introdotti da Java 5 è abbastanza semplice:

 public String name(String...newName){
// controllo se e' stato passato
// un parametro o meno
if( newName != null && newName.length == 1 )
// passato un parametro => metodo setter
this.name = newName[0];

// in ogni caso ritorno il valore
// corrente come metodo getter
return this.name;
}

Come si può notare il metodo si comporta come getter sempre, ovvero restituisce sempre il valore della proprietà name. Il metodo poi indovina il contesto di chiamata analizzando gli eventuali parametri: se ne sono stati passati al massimo uno allora il metodo si deve comportare come setter. E' possibile rilassare il test sul numero di parametri considerando valida una lista di parametri qualsivoglia lunga, e usando solo il primo come setter, ma questa scelta dipende dall'interfaccia che si vuole dare al metodo.
Il fatto che il metodo restituisca sempre, anche se chiamato in un contesto di setter, il valore della proprietà non è una limitazione, anzi consente di effettuare un method-chain invocando piu' metodi in cascata.

Nessun commento: