2009-02-24 5 views
10

Nel corso del tempo, il mio team ha creato una classe centrale che gestisce un agglomerato di responsabilità e corre a oltre 8.000 linee, tutte scritte a mano, non generate automaticamente.Come si fa il refactoring di una classe che viene costantemente modificata?

Il mandato è scaduto. Dobbiamo rifattore della classe dei mostri. La maggior parte del piano consiste nel definire categorie di funzionalità nelle proprie classi con una relazione has-a con la classe monster.

Ciò significa che un sacco di riferimenti che attualmente leggere in questo modo:

var monster = new orMonster(); 
var timeToOpen = monster.OpeningTime.Subtract(DateTime.Now); 

sarà presto leggere in questo modo:

var monster = new Monster(); 
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now); 

La domanda è: come mai si coordinano un tale cambiamento ? I riferimenti ai rifiuti "orMonster" ogni singola classe aziendale. Alcuni metodi sono chiamati letteralmente in migliaia di punti nel codice. È garantito che, ogni volta che facciamo una tale possibilità, qualcun altro (probabilmente più qualcun'altro) nella squadra avrà il codice estratto che chiama la proprietà .OpeningTime

Come si coordinano cambi di così grande scala senza rettifica di produttività fermarsi?

+0

Con Visual Studio, fare clic con il tasto destro del mouse su "Trova tutti i riferimenti ..." e sostituire. – core

risposta

27

Si dovrebbe fare il vecchio metodo chiamare il nuovo metodo. Quindi, nel tempo, modificare i riferimenti al vecchio metodo per chiamare il nuovo metodo. Una volta modificati tutti i riferimenti client, è possibile eliminare il vecchio metodo.

Per ulteriori informazioni, vedere Move Method nel classico di Martin Fowler, Refactoring.

+0

Questo. E contrassegnali come deprecati o quant'altro se la tua lingua ha quella caratteristica. Funzioneranno ancora, ma otterranno brutte sottolineature/barrate nell'IDE e riempiranno la console di avvertimenti. –

10

Una cosa che puoi fare è lasciare temporaneamente i metodi proxy nella classe mostro che delegherà al nuovo metodo. Dopo circa una settimana, una volta verificato che tutto il codice utilizza il nuovo metodo, è possibile rimuovere il proxy in modo sicuro.

1

Non effettuare il refactoring.

Ricominciare e seguire la legge di Demeter. Crea una seconda classe mostro e inizia da zero. Quando la seconda classe mostro è finita e funzionante, allora sostituisci le occorrenze del primo mostro. Scambialo. Spero che condividano un'interfaccia, o puoi farlo accadere.

E invece di questo: "monster.TimeKeeper.OpeningTime.Subtract (DateTime.Now)"

fare questo: monster.SubtractOpeningTime (DateTime.Now). Non uccidersi con dot-notation (da cui il demeter)

2

Suggerisci utilizzando uno strumento come nDepend per identificare tutti i riferimenti ai metodi di classe. L'output di nDepend può essere utilizzato per darti un'idea migliore su come raggruppare i metodi.

7

L'ho gestito prima di andare avanti e refactoring del codice, ma poi aggiungere metodi che corrispondono alla vecchia firma che inoltra le chiamate al nuovo metodo. Se si aggiunge l'attributo "Obsoleto" a questi metodi temporanei, il codice continuerà a generare sia con le vecchie chiamate di metodo che con le nuove chiamate di metodo. Poi nel tempo puoi tornare indietro e aggiornare il codice che chiama il vecchio metodo. La differenza qui è che durante la compilazione otterrai "Avvertenze" per aiutarti a trovare tutto il codice che richiede l'aggiornamento.

6

Non sono sicuro di quale lingua si sta utilizzando.Net è possibile creare avvisi del compilatore che ti permetteranno di lasciare i vecchi riferimenti per un periodo di tempo in modo che funzionino come previsto, ma di mettere un avviso per gli altri sviluppatori.

http://dotnettipoftheday.org/tips/ObsoleteAttribute.aspx

+0

Suggerimento eccezionale. Vorrei poter alzare il voto due volte. –

+0

Accettare questo come la risposta è 15 punti non è vero? Quasi altrettanto buono! ;) –

3

mantenere il vecchio metodo in atto e in avanti per il nuovo metodo (come altri hanno detto), ma anche inviare un messaggio di registro nel metodo di inoltro per ricordarsi di rimuoverlo.

Si potrebbe semplicemente aggiungere un commento ma è troppo facile perderselo.

1

Diverse persone hanno fornito buone risposte sull'orchestrazione del refactoring stesso. Questa è la chiave. Ma hai anche chiesto di coordinare i cambiamenti tra più persone (che credo fosse il punto cruciale della tua domanda). Quale controllo sorgente stai usando? Qualunque cosa come CVS, SVN, ecc. Può gestire le modifiche in entrata da più sviluppatori contemporaneamente. La chiave per farlo funzionare senza intoppi è che ogni persona deve rendere i propri commit granulari e atomici, e ogni sviluppatore dovrebbe attirare i commit di altre persone spesso.

2
var monster = new Monster(); 
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now); 

Non sono sicuro che dividerlo e renderne pubbliche solo alcune parti è meglio. Questo sta violando la legge di Demeter e può portare a dolore NullReference.

Suggerirei di esporre il cronometrista alle persone senza coinvolgere il mostro.

Se tutto andrà bene analizzando l'API e vedendo ciò che è possibile tagliare e incapsulare all'interno del mostro. Certamente dare ai giocattoli mostri con cui giocare anziché fare il mostro fa tutto il lavoro stesso è un buon invito. Lo sforzo principale è definire i giocattoli che il mostro ha bisogno per semplificare il suo lavoro.

+0

D'altra parte, questo potrebbe essere un punto di partenza: divulgarne le funzionalità e quindi rimuovere la classe mostro in un secondo momento. – TofuBeer

+0

Sì, ma sappiamo tutti che una "data successiva" di solito significa mai o più di un anno dopo. È meglio prendere il suggerimento di Chris Holme, creare una suite di test unitari e ricominciare e copiare il codice pezzo per pezzo nella nuova struttura. – Quibblesome

4

Sviluppa le modifiche in un ramo. Scomporre un sottoinsieme di codice in una nuova classe, apportare modifiche alla base del client, eseguire un test completo e quindi unire nuovamente.

Questo concentra la rottura a quando si uniscono - non l'intero ciclo di sviluppo.

Combina questo con il suggerimento di Patrick a have the monster call the small monsters. Ciò ti consentirà di ripristinare facilmente se il codice client unito interrompe le modifiche a quel client. Come dice Patrick, sarai in grado di rimuovere i metodi del mostro (ora stub) una volta provato che nessuno lo sta usando.

Ho anche eco il consiglio di diversi poster di esporre direttamente le classi scomposte, non tramite il mostro. Perché applicare solo mezza cura? Con lo stesso sforzo, potresti applicare una cura completa.

Infine: test di unità di scrittura. Scrivi molti test unitari. Oh, ragazzo, hai bisogno di test unitari per tirarlo fuori in sicurezza. Ho già detto che hai bisogno di test unitari?

+0

+1 I miei pensieri esattamente. – si618

-2

Una classe così enorme è davvero un problema. Da quando è diventato così grande e nessuno si è sentito a disagio, ci deve essere qualcosa di sbagliato nelle politiche del progetto. Direi che dovresti dividere in coppie e fare coppia di programmazione. Crea un ramo per ogni coppia di programmatori. Lavora per 1-2 giorni sul refactoring. Confronta i tuoi risultati. Questo ti aiuterà ad evitare la situazione in cui il refactoring andrebbe dall'inizio nella direzione sbagliata e finalmente ciò porterebbe alla necessità di riscrivere la classe dei mostri da zero.

1

Guarderò prima usando la classe parziale per dividere la singola classe mostro su molti file, raggruppando i metodi in categorie.

È necessario interrompere chiunque modifica la classe mostro mentre ci si divide nei file.

Da quel momento in poi è probabile che si verifichino meno conflitti di unione poiché ci saranno meno modifiche per ogni file. È quindi possibile cambiare ciascun metodo nella classe mostro, (un metodo per ogni controllo) per chiamare le nuove classi.

Problemi correlati