Guida Tecnica

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.

Codice prima e dopo che mostra i pattern extract method e rename refactoring

In questo articolo:

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