Viste le richieste che mi sono state fatte nei commenti al mio precedente post, ho deciso di dare qualche dettaglio in più sulle tecniche di refactoring che ho utilizzato nella realizzazione di IMHO.

Occorre innanzitutto tenere presente che nelle fasi iniziali di sviluppo ho installato il Resharper. Questo tool, prodotto dalla stessa azienda che ha realizzato uno degli IDE più famosi per Java (IntelliJ IDEA), fornisce un bel po' di strumenti utili per rendere più agevole la scrittura del codice in VS.NET 2003 e per fare del vero e proprio refactoring. Quella che preferisco in assoluto è la possibilità di aggiungere gli using automaticamente quando si inseriscono delle classi che non sono mai state utilizzate. Tuttavia dopo pochi giorni di lavoro mi sono visto costretto a disinstallare il tutto. A quanto pare Resharper ha la brutta abitudine di fagocitare quanto più RAM possibile e rende instabile e inutilizzabile Visual Studio. Questo probabilmente perchè ha la pretesa di sostituire totalmente l'intellisense di VS, invece che semplicemente aggiungere le funzionalità mancanti.

Quindi in breve mi sono trovato nudo. Nessuno strumento per poter fare del refactoring assistito, e la necessità come si suol dire ha aguzzato l'ingegno. La chiave di tutto si chiama CTRL+SHIFT+B. Per i due che non sanno di cosa parlo, questa è la combinazione di tasti che compila la solution. Tutta la tecnica, che nel titolo ho sintetizzato con un pomposo Error Driven Refactoring, si basa sul provocare errori di compilazione. Facciamo qualche esempio:

Cambiare il nome ad una variabile, ad un tipo, oppure a un metodo, è il caso più banale. Si va sul membro che si desidera cambiare, lo si cambia e si compila. A questo punto, nel Task Panel si presenteranno un certo numero di errori, talvolta qualche centinaio. Non sempre serve risolverli tutti, perchè se avete mai fatto caso il compilatore più di qualche volta genera due o tre avvisi per lo stesso errore. La mia tecnica prevede di risolvere al massimo la prima decina e poi ricompilare. E avanti così finche il codice non da più errori. Sembra una attività ingrata, ma una volta che si è presa confidenza è più veloce di quello che si possa immaginare. Non è detto che a questo punto il codice compilato funzioni. Ad esempio se usate la reflection per istanziare un tipo è probabile che in fase di compilazione non ci sia alcun errore, mentre quando lo eseguite ovviamente solleva una eccezione. Una bella ricerca testuale non guasta.

Cambiare la firma di un metodo è un'altra cosa abbastanza facile. Analogamente al caso precedente si provoca l'errore cambiando la firma. Talvolta conviene dapprima cambiare il nome del metodo e completare il primo passaggio. Poi si cambierà il tipo di ritorno oppure i parametri e si procede nuovamente alla compilazione. In questi casi occorre avere la cautela di accorgersi quando si cambia il tipo di un parametro con uno che sia compatibile, ad esempio con una classe padre. Il compilatore in questi casi non ci viene in aiuto, ma se contestualmente create una errore fittizio inserendo un carattere estraneo nel nome del metodo è più facile che si ritrovino tutte le chiamate. Inoltre bisognerà avere la cautela di capire che cosa succede nel corpo del metodo. Anche qui un carattere estraneo nel nome del parametro che occorre modificare aiuterà a trovare tutti gli usi che sono stati fatti e a verificare che non vi siano anche errori logici oltre che sintattici. In questo caso non occorre nemmeno ricompilare perchè sarà l'intellisense a indicarci l'errore con una simpatica sottolineatura.

Accomunare alcune classi con un'unica classe padre. Tipicamente in questi casi, si parte dapprima con la realizzazione della classe base vuota e immediatamente la si sostituisce a object nelle classi che dovranno estenderla. Nessun errore dovrebbe presentarsi. Però se si accomunano pià classi significa che si intende spostare uno o più metodi e proprietà ad un livello più basso. Perciò mano amano si fà cut & paste e si trasportano tali metodi nella nuova classe. Ad ogni spostamento si compila e così si individuano ad esempio i membri privati che sono referenziati da tali metodi e si trasportano anch'essi. In breve la classe dovrebbe cominciare ad indicare che alcuni metodi sono sovrascritti e che occorre l'uso dell'operatore new. Questi sono i metodi che sono rimasti nelle altre classi e vanno del tutto cancellati assieme ai membri privati che sono i più difficili da scovare, perchè non danno errore. Ad accomunare spesso è un'interfaccia o una classe astratta. Ne pimo case non c'è alcun problema perchè quando l'interfaccia è finita la si fa implementare alle classi e nel digitare Visual Studio autonomamente creerà i metodi e ad accorgersi se esistono già. Con le classi astratte è un po' più complesso. Si aggiungono i metodi astrtti e quindi si dovranno aggiungere gli override dove mancano oppure creare i metodi vuoi facendosi aiutare dagli errori e dall'intellisense.

Estrapolare delle classi da un progetto per crearne uno nuovo. Questa è la forma di refactoring che mi piace di più. Occorre tenere presente sempre che alcune classi possono essere usate da altri assembly, e che talvolta il codice che si scrive diventa un buon candidato al riutilizzo. Perciò trasportarlo in un altro assembly è vitale. Quindi si crea il nuovo progetto. Io ho l'abitudine di dare al progetto il medesimo nome del namespace che contiene. Questo è di enorme aiuto ad orientarsi nel trovare quello che si cerca. Tornando al problema a questo punto usando il Drag & Drop si trascinano le classi da un progetto all'altro e contestualmente si aggiunge la nuova reference. I progetto dovrebbe compilare immediatamente perchè nelle classi spostate è rimasto il namespace originale. Quindi occorre un bel replace. Si evidenzia il namespace da cambiare e si premo CTRL+H. Quindi ci si assicura di avere lo scope di progetto e si sostituisce con il namespace nuovo. Finalmente il compilatore comincia a dare errori. Questi potranno essere causati dalla mancanza di una reference oppure da uno using sbagliato. Non resta che aggiungere quello che serve e la solution tornerà a ricompilare.

Quelli che ho indicato sono solo alcuni dei casi da gestire. Indicarli tutti mi sembrava improponibile. Quello che mi interessa far capire è che non serve avere dei tool molto evoluti per potersi dedicare alla tecnica del refactoring. A mio parere nessun tool potrà mai arrivare a fare tutto ciò che ci serve, ma con un po' di fantasia si arriva a trovare una soluzione efficace.

Ora non avete più scuse. Refactor your life.

powered by IMHO