Extract Class Refactoring: Quando e Come Usarlo
Quando e come applicare l'extract class refactoring per decomporre le god class, ridurre il coupling e migliorare la coesione nel tuo codebase.
In questo articolo:
- Il problema della god class
- Quando applicare l’extract class refactoring
- Come eseguire l’operazione extract class in sicurezza
- Coupling, coesione e cosa cambia dopo
- Conclusione
L’extract class refactoring è l’operazione di prendere un sottoinsieme di campi e metodi da una classe esistente e spostarli in una nuova classe separata. La classe originale delega alla nuova. Il risultato sono due classi, ognuna con una responsabilità più chiara e più focalizzata di quella che aveva l’unica classe originale.
Questa operazione affronta uno dei problemi strutturali più comuni nei codebase legacy: la god class. Una god class è una classe che sa troppo e fa troppo. Accumula responsabilità nel tempo man mano che le funzionalità vengono aggiunte a qualunque classe sia più conveniente, fino a diventare il centro operativo dell’intero sistema e impossibile da cambiare senza rischi.
Il problema della god class
Le god class sono facili da identificare e difficili da accettare come problema. Sono facili da identificare perché hanno centinaia o migliaia di righe di codice, decine di metodi e nomi vaghi o sovraccarichi: Manager, Handler, Controller, Processor, Service. Sono difficili da accettare perché spesso contengono la logica di business più critica del sistema, accumulata nel corso degli anni.
Il costo operativo delle god class è concreto. Una classe di 3.000 righe con 60 metodi e 30 campi ha problemi di coupling: molte altre classi dipendono da essa, quindi qualsiasi modifica richiede il test di una grande porzione del sistema. Ha problemi di coesione: metodi non correlati condividono stato, rendendo il comportamento difficile da prevedere. Ha problemi di testabilità: il test unitario richiede l’istanziazione dell’intera classe e di tutte le sue dipendenze.
Quando applicare l’extract class refactoring
L’extract class è l’operazione giusta quando una classe mostra questi segnali:
Gruppi di campi usati insieme ma non con l’intera classe. Se una classe ha campi indirizzo_fatturazione, citta_fatturazione, paese_fatturazione insieme a campi per il catalogo_prodotti, conteggio_inventario e regole_spedizione, i campi di fatturazione formano un gruppo coesivo naturale che appartiene a una classe IndirizziFatturazione.
Gruppi di metodi che operano su un sottoinsieme dei campi della classe. Se i metodi calcola_sconto, applica_codice_promo e valida_coupon leggono e scrivono solo i campi tasso_sconto, codici_promo e storico_coupon, quei metodi e campi formano una naturale classe MotoreSconto.
La classe è difficile da descrivere senza la parola “e”. “Questa classe gestisce l’autenticazione utente e gestisce l’elaborazione degli ordini e calcola i costi di spedizione.” Ogni “e” è un candidato per l’estrazione.
La configurazione del test richiede dipendenze non correlate. Se testare la logica di calcolo dello sconto richiede l’inizializzazione di una connessione al database, un client mailer e un client API di terze parti, la classe ha troppe responsabilità mescolate.
Come eseguire l’operazione extract class in sicurezza
Passo 1: Identifica il cluster da estrarre. Scegli un gruppo di campi e metodi che appartengono insieme. Dai un nome alla nuova classe prima di iniziare; il nome rivelerà se il cluster è genuinamente coesivo. Se non riesci a nominare la nuova classe senza usare “e”, il cluster non è abbastanza coesivo.
Passo 2: Stabilisci test di caratterizzazione. Prima di spostare qualcosa, scrivi test che coprano il comportamento dei metodi che stai per spostare. Usa la tecnica descritta nella guida ai test di caratterizzazione.
Passo 3: Crea la nuova classe senza logica. Crea una classe vuota con il nome scelto. Sposta prima i campi, poi i metodi, in commit separati. Inizia con metodi privati/interni per evitare di cambiare immediatamente l’interfaccia pubblica.
Passo 4: Aggiorna la classe originale per mantenere un riferimento alla nuova classe. Sostituisci i campi e metodi spostati nella classe originale con un riferimento alla nuova classe e chiamate di delega. L’interfaccia pubblica della classe originale non deve cambiare: i chiamanti continuano a usare la stessa API.
Passo 5: Esegui i test dopo ogni passo. Qualsiasi fallimento ti dice esattamente quale spostamento ha introdotto un problema. Per questo motivo i passi devono essere separati.
Passo 6: Aggiorna i test per puntare direttamente alla nuova classe. Una volta che l’estrazione è stabile, scrivi o aggiorna i test unitari che puntano direttamente alla nuova classe. Questo completa il refactoring e ti lascia con test meglio isolati e più veloci.
Coupling, coesione e cosa cambia dopo
Dopo un’estrazione di classe riuscita, diverse cose misurabili migliorano.
La complessità ciclomatica diminuisce nella classe originale perché i metodi che usavano più rami all’interno della god class sono ora isolati in una classe più piccola.
L’isolamento dei test migliora. Puoi ora testare la classe estratta senza istanziare la god class. Se la classe estratta non ha dipendenze esterne proprie, può essere testata con test unitari puri.
Il coupling diventa esplicito. Prima dell’estrazione, le dipendenze erano implicite. Dopo l’estrazione, la classe originale ha una dipendenza esplicita sulla nuova classe. Puoi iniettare la nuova classe come dipendenza.
Il change failure rate diminuisce. Con due classi più piccole invece di una grande, le modifiche alla logica degli sconti non rischiano più di rompere la logica di autenticazione. Il raggio d’azione di ogni cambiamento si riduce.
Conclusione
L’extract class refactoring è lo strumento principale per decomporre le god class. Applicalo quando vedi gruppi di campi o metodi che appartengono coesivamente insieme ma sono mescolati con logica non correlata nella stessa classe. Eseguilo in modo incrementale: crea la nuova classe, sposta i campi, sposta i metodi, delega, verifica.
Il risultato non è solo codice più pulito. È codice con complessità misurabilmente inferiore, migliore isolamento dei test e ridotto rischio operativo. I team che applicano questo pattern in modo consistente riducono il numero di classi sopra 500 righe del 60-70% entro un trimestre di lavoro focalizzato.
Hai un codebase con questi problemi? Parliamo del tuo sistema