L'idea che mi è venuta è stata quella di approfittare di questo task banale per sperimentare un aspetto della programmazione java che molto mi affascina, ma che troppo raramente ho occasione di utilizzare: la reflection.
Ho creato un bean avente ogni proprietà (con relativo setter) che si chiama proprio come un elemento "property" del file xml.
A questo punto ho sfruttato la potenza della reflection per inserire la lettura delle proprietà all'interno di un ciclo for, col vantaggio di scrivere il codice una volta sola per tutte le proprietà.
(avrei potuto arrivare allo stesso risultato utilizzando una HashMap come struttura dati, ma a quel punto dove sarebbe stato il divertimento? edit: e comunque le due soluzioni hanno pro e contro)
Quindi per prima cosa creaimo il bean:
public class MyBean {
private String nome;
private String cognome;
//aggiungere tutte le proprietà del file xml, con relativi setters e getters
public String getNome(){return nome;}
public String getCognome(){return cognome;}
public String setNome(String nome){this.nome = nome;}
public String setCognome(String cognome){this.cognome = cognome;}
}
Il cuore del ciclo for è riportato di seguito:
MyBean bean = new MyBean();
//Per ogni proprietà
for (int j=0; j < propertyElements.size(); ++j) {
Element property = (Element)propertyElements.item(j);
name = property.getAttribute("id");
value = ((Element)property.getElementsByTagName("value").item(0)).getAttribute("id");
try {
Method setter = bean.getClass().getMethod(getMethodName(name), new Class[] { value.getClass( ) });
setter.invoke(bean, new Object [] { value } );
}
catch (Exception e) {
//Mi limito a loggare l'eccezione ignorando la proprietà non prevista
log.error("Error (" + name + ")",e);
}
}
dove il metodo getMethodName è così definito
//name -> getName
private String getMethodName(String name) {
return new StringBuilder("get")
.append(name.substring(0,1).toUpperCase())
.append(name.subString(1)).toString;
}
Analizziamo il codice nel blocco try-catch:
nella prima riga viene costruito l'oggetto Method, che rappresenta il metodo che verrà invocato. Per fare questo si invoca il metodo getMethod() sulla classe Bean. Tale metodo riceve due argomenti: una stringa contenente il nome del metodo, ed un array di Class, che rappresentano i tipi dei parametri formali (non i valori attuali) che il metodo si aspetta. Questo secondo parametro è importantissimo perchè permette di gestire l'overloading dei metodi.
E' nella seconda linea che il metodo viene invocato, tramite la chiamata al metodo invoke. Questa riceve due parametri: il primo è il riferimento all'oggetto su cui il metodo deve essere invocato, il secondo è un array contenente i parametri attuali. Il metodo ritorna un object contenente il risultato dell'invocazione (che io ho ignorato essendo il mio getXXX dichiarato void).
Questo codice è stato inserito in un blocco try-catch perchè l'uso della reflection può sollevare diverse eccezioni, che dettagliano il problema nel caso l'invocazione fallisse:
SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
Per avere maggiori informazioni su queste eccezioni si rimanda alla documentazione del JDK.
Chiudo sottolineando quelli che sono i due grossi svantaggi dell'utilizzo della reflection:
- il primo è legato alle prestazioni, in quanto la chiamata ad un metodo tramite reflection è meno performante dell'invocazione diretta del metodo;
- il secondo problema è legato alle potenzialità che la reflection offre: l'uso della reflection può nascondere al programmatore cosa realmente accade nel codice, rendendo più difficoltose le attività di manutenzione dello stesso

Nessun commento:
Posta un commento