Tecniche di Refactoring: I 10 Pattern Più Utili con Esempi
Le 10 tecniche di refactoring più pratiche con esempi prima e dopo: extract method, extract class, move method, rename e altre.
In questo articolo:
- Perché questi 10 pattern
- Pattern di estrazione
- Pattern di organizzazione
- Pattern di semplificazione
- Conclusione
Le tecniche di refactoring sono trasformazioni nominate e ripetibili che cambiano la struttura del codice senza cambiare il comportamento. Usare pattern nominati invece di riscritture ad hoc ha due vantaggi: la trasformazione è ben compresa e prevedibile, e gli IDE possono automatizzare molte di esse, eliminando il rischio di introdurre errori attraverso la modifica manuale del testo.
Questa guida copre i 10 pattern di refactoring che forniscono il maggior valore nei codebase reali. Ognuno include una breve spiegazione, un esempio prima e dopo e note su quando applicarlo.
Perché questi 10 pattern
Il catalogo originale di Fowler contiene oltre 60 pattern. La maggior parte dei codebase ne ha bisogno di circa 10 per l’80% del tempo. I pattern qui selezionati si basano sulla frequenza d’uso nelle effettive revisioni del codebase: affrontano i problemi strutturali più comuni che causano alta complessità ciclomatica, bassa coesione e frizione nella manutenzione.
Se stai lavorando a un programma di soluzione al debito tecnico, queste sono le tecniche che i tuoi ingegneri useranno più spesso.
Pattern di estrazione
1. Extract Method
Il pattern di refactoring più comunemente necessario. Quando una funzione è troppo lunga o fa più di una cosa, estrai una sezione in un nuovo metodo con un nome significativo.
Prima:
def process_order(order):
# valida
if order.total <= 0:
raise ValueError("Totale non valido")
if not order.customer_id:
raise ValueError("Cliente mancante")
# calcola sconto
discount = 0
if order.total > 100:
discount = order.total * 0.1
order.total = order.total - discount
save(order)
Dopo:
def process_order(order):
validate_order(order)
apply_discount(order)
save(order)
def validate_order(order):
if order.total <= 0:
raise ValueError("Totale non valido")
if not order.customer_id:
raise ValueError("Cliente mancante")
def apply_discount(order):
if order.total > 100:
order.total -= order.total * 0.1
Applica quando una funzione supera 20-30 righe, quando hai bisogno di aggiungere un commento per spiegare un blocco di codice (il testo del commento diventa il nome del metodo), o quando una sezione di codice potrebbe essere riutilizzata altrove.
2. Extract Variable
Sostituisci un’espressione complessa con una variabile nominata per rendere chiara l’intenzione.
Prima: if user.age >= 18 and user.country in PAESI_CONSENTITI and not user.is_banned:
Dopo: is_eligible = user.age >= 18 and user.country in PAESI_CONSENTITI and not user.is_banned
3. Extract Class
Quando una classe ha troppe responsabilità, estrai un sottoinsieme di campi e metodi in una nuova classe. Questo è il pattern più impattante per ridurre il coupling.
4. Extract Interface / Abstract Class
Quando più classi condividono comportamenti, estrai un’interfaccia che cattura il contratto condiviso. Questo abilita la dependency injection e rende il codice testabile senza dipendere da implementazioni concrete.
Pattern di organizzazione
5. Move Method
Se un metodo usa dati da un’altra classe più che dalla propria, spostalo nella classe che usa di più. Questo migliora la coesione e riduce il coupling tra le classi.
6. Rename
Il pattern più semplice e più impattante per la leggibilità. Rinomina variabili, metodi e classi per riflettere accuratamente il loro scopo. Una funzione chiamata process() che in realtà valida i dati di pagamento dovrebbe chiamarsi validate_payment(). Gli IDE moderni rendono il rename refactoring sicuro aggiornando automaticamente tutti i riferimenti.
7. Introduce Parameter Object
Quando una funzione riceve più di tre o quattro parametri, raggruppa i parametri correlati in un oggetto.
Prima: def create_user(nome, cognome, email, telefono, paese):
Dopo: def create_user(info_contatto: InfoContatto):
Questo riduce le firme delle funzioni, rende il codice più facile da leggere e facilita l’aggiunta di logica di validazione al gruppo di parametri.
8. Replace Magic Number with Named Constant
Valori numerici e stringhe letterali sparsi nel codice oscurano l’intento e rendono i cambiamenti rischiosi.
Prima: if response.status_code == 429:
Dopo: if response.status_code == HTTP_TROPPE_RICHIESTE:
Pattern di semplificazione
9. Replace Conditional with Polymorphism
Quando un metodo ha una catena switch o if-else che si dirama su un tipo o categoria, sostituisci il condizionale con una gerarchia di classi.
Prima:
def calcola_spedizione(ordine):
if ordine.tipo == "standard":
return ordine.peso * 2.5
elif ordine.tipo == "express":
return ordine.peso * 5.0
elif ordine.tipo == "notturno":
return ordine.peso * 10.0
Dopo: ogni tipo di spedizione è una classe con un metodo calcola(). Il condizionale scompare; il metodo giusto viene chiamato tramite polimorfismo.
Questo è il pattern corretto quando la lista dei tipi cresce nel tempo, poiché aggiungere un nuovo tipo richiede solo una nuova classe piuttosto che modificare un’istruzione switch esistente.
10. Decompose Conditional
Quando una condizione booleana complessa viene ripetuta o è difficile da capire, estraila in un metodo nominato il cui nome spiega la condizione.
Prima: if inizio <= data <= fine and not festivo and regione in regioni_attive:
Dopo: if e_periodo_servizio_attivo(data, regione):
Conclusione
Questi 10 pattern di refactoring affrontano la maggior parte dei problemi strutturali nei codebase reali. Extract method da solo elimina la maggior parte dei problemi di funzioni sovradimensionate. Rename migliora la leggibilità in tutto il codebase nel tempo. Extract class e move method insieme affrontano i problemi di coupling e coesione che rallentano lo sviluppo.
La chiave per applicare questi pattern in sicurezza è usare l’automazione dell’IDE dove disponibile, lavorare un pattern alla volta e verificare con i test dopo ogni cambiamento. Applicati in modo consistente, queste tecniche riducono la complessità ciclomatica, migliorano la copertura dei test e rendono i codebase che un tempo richiedevano settimane per essere navigati leggibili in giorni.
Hai un codebase con questi problemi? Parliamo del tuo sistema