Primi utilizzi di IntelliJ IDEA: refactoring di un metodo
Dopo la positiva esperienza del Java IDE Day, ho scaricato e cominciato ad utilizzare IntelliJ IDEA di JetBrains. So che è un’IDE molto potente e quindi la mia aspettativa è di apprenderla poco per volta. In questo blog analizzo una sola funzionalità, il refactoring di un metodo, per illustrare un po’ la filosofia di IDEA e alcune differenze rispetto alle altre IDE.
Come punto di partenza utilizzerò il seguente codice.

La classe MyClass ha un metodo print() che stampa una coppia chiave/valore su un PrintStream. Lo stream, la chiave e il valore sono forniti come parametri.
Il metodo printToSystemOut() fa una cosa simile, con la differenza che lo stream non è richiesto come parametro ma viene utilizzato sempre lo standard output di system (System.out);
Il metodo statico main(), infine, mostra un’invocazione del metodo precedente con chiave ‘temperatura’ e valore ‘12.5′.
Ok, passiamo a qualche refactoring.
Cambiare l’ordine dei parametri
Supponiamo di voler cambiare l’ordine dei parametri accettati dal metodo print(). Vogliamo che i parametri da (out, key, value) diventino (key, value, out). Con il mouse posizionato sul nome del metodo, premiamo il tasto destro e nel menu “Refactor” selezioniamo la voce “Change signature…”. Comparirà una schermata come segue (fare click per vedere l’immagine in dimensioni reali).
![]()
Impressionante? Chi sapeva che ci potevano essere coì tante opzioni per un semplice refactoring alzi la mano :-)
Guardiamo nel dettaglio. Nel pannello con la lista dei parametri abbiamo la possibilità di aggiungere, rimuovere e riordinare i parametri. A noi serve la funzione di riordino, quindi selezioniamo il parametro “out” e facciamo due volte click su “Move down” per spostarlo in fondo alla lista. Fin qui nulla di particolare, ma aspettiamo a premere il bottone “Refactor”.
Notiamo in alto a sinistra le opzioni “modify” e “delegate via overloading method”. E’ interessante sapere cosa fanno entrambe, per cui ho eseguito due refactoring alternativi mostrando qui sotto i due diversi risultati.
“Modify” produce:

… che è la classica rifattorizzazione dove l’ordine dei parametri è cambiato sia nella definizione di print() che nelle sue invocazioni.
“Delegate via overloading method” produce:

Qui ci sono tre cose da notare:
- Il metodo
print()ha effettivamente cambiato l’ordine dei suoi parametri (out per ultimo). - E’ comparso un altro metodo
print()che ha l’ordine vecchio dei parametri (out per primo) ed è implementato in termini dell’altro metodoprint(). - In
printToSystemOut()l’invocazione diprint()non è cambiata (out rimane primo paramero).
Questo refactoring è interessante perché mantiene la compatibilità dell’API: c’è sia il metodo con la signature nuova sia quello con la signature vecchia. Se il nostro programma fosse una libreria, eventuali programmi che dipendono da essa continuerebbero a funzionare senza modifiche.
Aggiungere un’eccezione
Torniamo al programma com’era in partenza e pensiamo ad un’altra fattorizzazione. Vogliamo che print() lanci un’eccezione (nel codice che segue dichiarerò l’eccezione senza mai lanciarla effettivamente, ma possiamo immaginare di aggiungere qualche verifica sui parametri - tipo value == null - che effivamente lanci un’eccezione). Dobbiamo modificare la signature aggiungendo l’opportuna clausola “throws”.
Nel pannello di “Change signature” (sempre lo stesso, quello visto prima) c’è una sezione in basso a sinistra dove si possono aggiungere, togliere o riordinare le eccezioni dichiarate dal metodo. Aggiungiamo l’eccezione Exception e premiamo “Refactor” per vedere come cambia il codice:

Notiamo tre cose:
- Il metodo
print()ha effettivamente acquisito una dichiarazione di eccezione nella sua signature. - Il metodo
printToSystemOut()ha acquisito un blocco try-catch per incapsulare la chiamata a print(). InoltreprintToSystemOut()non lancia eccezion, esattamente come prima. - Il metodo
main()è rimasto invariato.
Analizziamo la nuova situazione: print() può lanciare eccezioni e printToSystemOut() le intercetta, le stampa tramite e.printStackTrace() ma non le propaga. La domanda è: è giusto che printToSystemOut() non propaghi l’eccezione? La risposta è… “dipende”. Ma in questo caso, print() e printToSystemOut() sono abbastanza simili e sarebbe forse giusto che se una lancia eccezioni, lo faccia anche l’altra e che il compito di intercettarle spetti a main().
Benissimo, facciamo “undo” della fattorizzazione più recente e torniamo al pannello “Change signature”. Aggiungiamo nuovamente la dichiarazione di eccezione Exception come abbiamo già fatto prima e notiamo il bottone “Propagate exception”, su cui facciamo click. Comparirà un nuovo pannello (fare click per vedere in dimensioni reali).
IntelliJ IDEA mostra un albero delle chiamate per il metodo in questione (print()). In questo caso mostra la chiamata da printToSystemOut(), a sua volta chiamato da main(). La cosa interessante è che possiamo selezionare fino a quale livello di chiamata propagare l’eccezione. In questo esempio ho deciso di propagare fino a printToSystemOut(), come è visibile dalle checkbox selezionate.
Premiamo “Ok”, quindi “Refactor”. Il codice risulterà modificato come segue.

Vediamo che:
print()lancia eccezioneprintToSystemOut()propaga l’eccezionemain()gestisce l’eccezione tramite un blocco try-catch
Rispetto al refactoring precedente, qui l’eccezione è gestita “dal chiamante del chiamante” portandoci più vicini all’idea che deve essere main() a gestire le eccezioni.
Altre funzioni di refactoring
Scorro velocemente le altre funzioni disponibili dal pannello “Change signature”.
E’ possibile cambiare le dichiarazioni di base relative al metodo, come il nome, il tipo restituito e la visibilità.
Quando si aggiungono parametri è possibile dichiarare un valore di default fisso che verrà impostato in tutte le invocazioni chiamanti. Alternativamente è possibile chiedere ad IDEA (checkbox “Any var”) di cercare, nel corpo dei chiamanti, una variabile compatibile che funga da parametro effettivo per il nuovo parametro formale inserito.
Similmente a quanto visto per le eccezioni, è possibile fare in modo che nuovi parametri siano gestiti dai chiamanti diretti oppure dai chiamanti ad un qualunque livello di profondità (pannello “Propagate parameters”). Se per esempio aggiungessimo un parametro a print() potremmo decidere se il suo valore è fornito da printToSystemOut() oppure da main().
Conclusioni
Se i problemi di cui ho parlato (mettere mano al codice con l’intenzione di migliorarlo o estenderlo tramite refactoring) sono il vostro pane quotidiano allora IntelliJ IDEA può dare una grossa mano.
A parte questo, la mia valutazione di IDEA è appena iniziata, quindi per conclusioni più approfondite bisogna aspettare :-)


Marzo 16th, 2008 11:20
Sono uno studente di ingegneria. Ero presente all’IDE Day.
La presentazione di intelliJ è stata interessante. Soprattutto la velocità con cui lo speaker scriveva codice utilizzando solo la tastiera.
Marzo 16th, 2008 12:10
Dimenticavo…
Ho scritto circa un anno fa due articoli sul refactoring con NetBeans 5.5:
http://www.predonzani.com/blog/archives/51
http://www.predonzani.com/blog/archives/54
Marzo 22nd, 2008 22:17
[…] l’aiuto dell’ottimo IntelliJ IDEA già provato in un precedente blog, scriviamo un bean per rappresentare dei […]