El camí sa cap als patrons mai no va ser dissenyar-los per endavant: és arribar-hi des de codi simple quan el dolor apareix — ho venim repetint des de la balança i ho vam convertir en mètode a 05-01. Aquesta lliçó ensenya la meitat que faltava: la mecànica. Refactoritzar és canviar l'estructura del codi sense canviar-ne el comportament, i fer-ho cap a un patró sobre codi viu — que produeix, que factura, que té usuaris — exigeix xarxa de seguretat, passos petits que compilen sempre, i criteri per saber quan l'esforç no compensa. Treballarem amb el catàleg de transformacions típiques, cadascuna amb el seu abans/després a PideYa.

Contingut

  1. Què és refactoritzar (i què no)
  2. La precondició innegociable: tests
  3. La disciplina: passos petits que sempre compilen
  4. Catàleg de refactoritzacions cap a patrons
  5. Quan NO refactoritzar
  6. Deute tècnic: el llenguatge del cost/benefici
  7. Exercicis i conclusió

Què és refactoritzar (i què no)

Definició de Martin Fowler (Refactoring, 1999; 2a ed. 2018): canviar l'estructura interna del codi sense canviar-ne el comportament observable. Les dues meitats importen:

  • Si canvies comportament (arregles un bug, afegeixes una funció), no estàs refactoritzant: estàs desenvolupant. Barrejar totes dues coses al mateix commit és la recepta del "no sé què ha trencat això".
  • Si no hi ha xarxa que verifiqui que el comportament es conserva, no estàs refactoritzant: estàs reescrivint i creuant els dits.

Joshua Kerievsky (Refactoring to Patterns, 2004) hi va afegir la peça que uneix aquest curs: els patrons no són només una destinació de disseny, són destinacions de refactorització — cadascun té seqüències de passos segurs per arribar cap a ell (i a vegades per marxar-ne, quan sobra: ho veurem als antipatrons). Tots dos llibres estan comentats a la bibliografia del curs.

La precondició innegociable: tests

Abans de moure una línia, necessites poder respondre "encara funciona?" en segons. Tres escenaris:

  1. Hi ha tests que cobreixen la zona: endavant.
  2. No hi ha tests, però el codi és testable: escriu primer tests de caracterització — tests que documenten el que el codi fa avui, incloses les seves rareses. No jutgen si és correcte; fotografien el comportament actual per detectar qualsevol desviació.
  3. No hi ha tests i el codi és un nus intestable (dependències estàtiques, base de dades, singletons...): usa un golden master — executa el codi amb un lot ampli d'entrades, desa les sortides com a "master", i després de cada pas compara byte a byte.
// Test de caracteritzacio sobre el calcul de comissions llegat de PideYa.
// COMPTE: no afirmem que 4.15 sigui CORRECTE — afirmem que es el que fa AVUI.
@Test
void caracteritzacio_comissioRestaurantEstandard() {
    var calculadora = new CalculadoraComissioLlegada();
    // Valors obtinguts EXECUTANT el codi actual, no de l'especificacio:
    assertEquals(new BigDecimal("4.15"), calculadora.calcular(comandaDe("27.90"), "ESTANDARD"));
    assertEquals(new BigDecimal("0.00"), calculadora.calcular(comandaDe("0.00"), "ESTANDARD"));
    assertEquals(new BigDecimal("2.50"), calculadora.calcular(comandaDe("27.90"), "PREMIUM"));
}

Si en escriure'ls descobreixes un comportament que sembla un bug: anota'l i conserva'l. S'arregla després, en un commit propi; durant la refactorització, el bug reproduït és part del contracte ("mateix comportament" inclou els defectes).

La disciplina: passos petits que sempre compilen

La regla d'or: entre commit i commit, el codi compila i els tests passen. Mai no hi ha un "estat intermedi trencat que arreglaré al final" — perquè aquest final a vegades no arriba (arriba una urgència, i la branca mor). El cicle:

flowchart LR
    A[Tests en verd] --> B[UN pas petit:<br/>extreure, moure, introduir]
    B --> C{Compila i<br/>tests en verd?}
    C -- Si --> D[Commit]
    C -- No --> E[Revertir el pas<br/>no depurar sobre vermell]
    E --> B
    D --> F{Hem arribat<br/>al patro?}
    F -- No --> B
    F -- Si --> G[Neteja final + commit]

Els passos són els moviments atòmics de Fowler, que a més el teu IDE automatitza (i una refactorització automàtica de l'IDE és més segura que una de manual): Extract Method, Extract Class/Interface, Move Method, Introduce Parameter Object, Replace Constructor with Factory Method, Replace Conditional with Polymorphism. Un patró s'assoleix encadenant mitja dotzena d'aquests moviments — mai d'un salt.

Catàleg de refactoritzacions cap a patrons

Olor (símptoma) Refactorització Patró destinació
switch/if-else sobre un "tipus" repetit en diversos mètodes Replace Conditional with Polymorphism Strategy o State
Condicional que decideix quina classe instanciar, duplicat Replace Constructor/Conditional with Factory Factory Method
Constructor telescòpic (sobrecàrregues creixents) Introduce Builder Builder
Classe déu que orquestra i executa tot Extract Class + façana sobre l'extret Facade + col·laboradors
Mètodes gairebé idèntics que difereixen en passos Form Template Method Template Method

Switch de tipus → Strategy (o State)

L'olor més comuna. A PideYa, abans de la lliçó 04-10, el càlcul d'enviament era:

// ABANS: el mateix switch apareixia tambe a estimarTemps() i a descripcioTarifa()
public BigDecimal calcularEnviament(Cistella cistella, Adreca adr) {
    switch (tipusTarifa) {
        case DISTANCIA: return tarifaBase.add(preuPerKm.multiply(distancia(adr)));
        case PLANA:     return new BigDecimal("2.99");
        case GRATIS:    return BigDecimal.ZERO;
        default:        throw new IllegalStateException();
    }
}

La seqüència segura (tests de caracterització primer, commit després de cada pas):

  1. Extract Method de cada branca: calcularPerDistancia(), calcularPlana()... El switch queda com un despatxador trivial. Compila, verd, commit.
  2. Extract Interface CalculEnviament amb calcular(cistella, adr) i una implementació per branca, movent cada mètode extret a la seva classe. El switch ara tria la classe en comptes d'executar la lògica. Verd, commit.
  3. Substituir el switch per l'objecte: el context rep un CalculEnviament (injectat o resolt un cop) i delega. Els switches dels altres mètodes cauen un a un igual. Verd, commit.
  4. Neteja: eliminar l'enum si ja no l'usa ningú, o deixar-lo només com a clau de configuració → estratègia.

I State? Mateixa mecànica exacta quan el "tipus" del switch és una etapa amb transicions (l'EstatComanda de 04-09 va néixer així d'un enum amb quatre switches). El diagnòstic de quin toca el dóna l'acarament de 04-13; la refactorització és la mateixa.

Condicionals de creació → Factory

Quan if (mercat.equals("ES")) new PassarellaRedsys() else new PassarellaConekta() apareix per segona vegada: Extract Method del condicional cap a un crearPassarella(mercat), després Move Method a una classe fàbrica (o mapa de Suppliers com el RegistreNotificadors), després substituir cada duplicat per la crida. Tres passos, tres commits — i l'evolució new → simple factory → Factory Method de 02-03 recorreguda sobre codi real. Si els condicionals creaven famílies coordinades, el mateix camí desemboca en Abstract Factory.

Constructor telescòpic → Builder

Comanda(client), Comanda(client, cupo), Comanda(client, cupo, notes, programada)... El camí sense trencar ningú:

  1. Crear el Builder al costat dels constructors existents, delegant en el més complet. Verd, commit.
  2. Migrar els punts de crida un a un cap al builder (cada migració compila sola). Commits.
  3. @Deprecated als constructors telescòpics; quan el darrer ús desapareix, eliminar-los i moure les validacions a build(). Verd, commit final.

El pas 1 és la tècnica general per a APIs amb molts consumidors: construir la destinació en paral·lel, migrar gradualment, enderrocar el vell al final — mai un big bang.

Classe déu → Facade + extraccions

El GestorComandes llegendari de 800 línies (validava, calculava, cobrava, notificava, imprimia). No es "converteix en façana": es buida. Seqüència: Extract Class de cada responsabilitat cohesionada (ValidadorComandes, CalculadoraQuanties, ServeiCobrament...), deixant a GestorComandes només la coordinació — que al final és una Facade legítima, sovint amb el nom canviat a FacanaCheckout perquè el nom expliqui la veritat. La diferència amb la classe déu original: ja no executa, orquestra; cada peça extreta és testable sola.

If/else de format → Template Method

Els informes de tancament de 04-11: tres mètodes gairebé clònics (generarCsv, generarPdf, generarHtml) que carregaven i agregaven igual però formataven diferent. Form Template Method: igualar la forma dels tres (mateixos passos, mateix ordre), Extract Method de cada pas, pujar els comuns a una superclasse abstracta, deixar els variables com a abstractes. Si més endavant els passos variables necessiten combinar-se en calent, la destinació evoluciona cap a Strategy — la frontera de l'acarament herència/composició.

Quan NO refactoritzar

Refactoritzar té cost (temps, risc, revisió) i només paga si el codi canviarà. No refactoritzis:

  • Codi estable que ningú no toca: si el mòdul d'exportació comptable fa tres anys que no té ni un canvi i no n'hi ha cap de previst, el seu switch lleig no fa mal a ningú. La lletjor no és deute si ningú no en paga interessos.
  • Codi que morirà: si la integració es reemplaça el trimestre vinent, embellir-la és amortitzar un cotxe camí del desballestament.
  • Sense xarxa de seguretat i sense possibilitat de construir-la ara: anota el deute i espera un moment millor — refactoritzar a cegues converteix "codi lleig que funciona" en "codi bonic que potser no".
  • Al mig d'una altra cosa: la "refactorització oportunista" que engreixa un commit de bug amb 40 fitxers reanomenats. Regla pràctica: refactor i feature, commits (idealment PRs) separats.

Deute tècnic: el llenguatge del cost/benefici

La metàfora de Ward Cunningham dóna l'idioma per decidir i per explicar-ho a negoci: el disseny subòptim és un préstec (lliurar abans a canvi d'estructura pitjor) i el seu interès és el sobrecost de cada canvi futur en aquesta zona. D'aquí les regles de gestió:

  • L'interès només es paga en codi que canvia. Per això la refactorització es prioritza per freqüència de canvi × dolor per canvi, no per lletjor. Un git log --since="6 months ago" --name-only et diu on són els punts calents; creuar-ho amb "on patim" assenyala el millor euro invertit.
  • Amortitza amb la regla del boy scout: deixa el codi una mica millor de com el vas trobar, a la zona que ja estàs tocant. Petit, continu, sense demanar permís.
  • Les refactoritzacions grans es paguen com a projectes: amb objectiu ("poder afegir un mercat nou en dies i no en setmanes"), no com a "netejar codi" — negoci compra capacitat, no estètica.

Errors Comuns i Consells

  • Refactoritzar sense xarxa ("és un canvi petit, què pot sortir malament"). Els tests de caracterització s'escriuen en una tarda; el bug en producció es paga en setmanes de confiança.
  • El big bang: branca refactor-total de tres setmanes que no compila fins al dia 15 i mor en conflictes. Passos petits, integració contínua, sempre en verd.
  • Canviar comportament d'amagat ("ja que hi sóc, arreglo això"). Trenca el contracte de la caracterització i embruta el diff. Bug a part, commit a part.
  • Refactoritzar cap al patró equivocat per saltar-se el diagnòstic: el mètode de 05-01 va abans que la mecànica d'aquesta lliçó. La seqüència és símptoma → diagnòstic → destinació → passos.
  • Passar-se de destinació: l'olor demanava Strategy i va acabar havent-hi Strategy + Factory + Observer "ja posats". Cada patró extra necessita el seu propi símptoma — si no, acabes de fabricar el material de la pròxima lliçó.
  • Consell: usa les refactoritzacions automàtiques de l'IDE (Rename, Extract Method/Interface, Move) sempre que existeixin: són transformacions verificades que no trenquen referències.
  • Consell: posa nom als commits de la seqüència ("pas 2/5: extreure interficie CalculEnviament") — el revisor t'ho agrairà i tu podràs revertir quirúrgicament.

Exercicis

Exercici 1: planificar la seqüència

El mètode generarTicketCuina(Comanda c, String format) de PideYa té un if-else sobre format ("TERMICA_58", "TERMICA_80", "PANTALLA") amb un 70% de codi duplicat entre branques (capçalera i peu idèntics, cos diferent). Hi ha dos tests que cobreixen "TERMICA_58" i cap més. Escriu el pla complet: (a) quina xarxa de seguretat construeixes primer i com, (b) el patró destinació amb el seu diagnòstic, (c) la seqüència de passos amb els seus punts de commit.

Exercici 2: refactoritzar o no?

Decideix i justifica amb cost/benefici:

  1. El parser d'un format de fitxer que un únic proveïdor va deixar d'emetre; es manté "per si de cas" i no es toca des del 2024.
  2. La classe Checkout s'ha modificat en 14 dels darrers 20 sprints; cada canvi de forma de pagament obliga a tocar 5 mètodes amb condicionals sobre tipusPagament, i ja hi ha hagut dues regressions.
  3. Un company proposa migrar tots els getters/setters del projecte a records "per modernitzar", 300 classes, sense cap canvi funcional previst en la majoria.

Exercici 3: la trampa del pas gran

Durant la migració del constructor telescòpic de Restaurant al Builder, un company proposa: "esborro ja els quatre constructors vells i arreglo els 60 errors de compilació aquesta tarda". Explica (a) els dos riscos concrets d'aquest pla davant de la migració gradual, (b) quin senyal del cicle de la lliçó està violant, (c) com reconduir-lo mantenint el seu objectiu d'acabar aquesta setmana.

Solucions

Solució 1: (a) Tests de caracterització per a "TERMICA_80" i "PANTALLA" (els que falten): executar el codi actual amb comandes representatives (amb extres, amb notes, sense notes) i fixar les sortides exactes com a esperades — si el tiquet és text multilínia, un golden master per fitxer comparat línia a línia és encara més còmode. (b) Diagnòstic: flux fix (capçalera → cos → peu) amb passos variables per format i sense necessitat de canviar en calent → Template Method (acarament amb Strategy pel mig). (c) Seqüència: 1. igualar la forma de les tres branques (mateixos passos en el mateix ordre) — verd, commit; 2. Extract Method per pas (imprimirCapcalera, imprimirCos, imprimirPeu) — verd, commit; 3. crear TicketCuina abstracta amb el mètode plantilla generar() i una subclasse per format, movent els cossos — verd, commit; 4. substituir l'if-else per la selecció de subclasse (probablement un minifactory, segon símptoma, patró propi) — verd, commit; 5. esborrar el mètode original — verd, commit final.

Solució 2: (1) No: codi congelat sense canvis previstos — el deute sense interessos no s'amortitza; com a molt, documentar que és candidat a esborrat. (2) Sí, i amb prioritat: freqüència de canvi altíssima × dolor provat (5 mètodes, 2 regressions) — és el punt calent exacte que la mètrica busca; destinació probable Strategy per a tipusPagament (més potser un Factory per a la seva creació), amb caracterització prèvia dels fluxos de pagament. (3) No com a projecte: canvi massiu sense canvi funcional previst = cost i risc avui, benefici especulatiu; reconduir cap a la regla del boy scout — migrar a record les classes que ja es toquin per un altre motiu, i només on el record no alteri el contracte (equals/hashCode canvien!).

Solució 3: (a) Risc 1: aquestes "tardes de 60 errors" no compilen fins al darrer arranjament — si sorgeix una urgència a mig fer, la branca queda morta o es força un merge a mitges; risc 2: 60 arranjaments mecànics sota pressió són 60 oportunitats de variar subtilment la semàntica (aquell constructor aplicava un default que el builder no aplica?) sense que la compilació ho detecti. (b) Viola "entre commit i commit el codi compila i els tests passen" — l'estat trencat prolongat és exactament el que el cicle prohibeix. (c) Mateix objectiu, una altra mecànica: builder delegant en el constructor complet avui (verd en una hora), migrar els 60 usos en tandes petites durant la setmana (cada tanda compila i es commiteja sola, es pot repartir entre l'equip), @Deprecated el dimecres, esborrar el divendres quan l'IDE confirmi zero usos.

Conclusió

Refactoritzar cap a patrons és una disciplina completa: xarxa de tests (de caracterització o golden master quan no n'hi ha), passos atòmics que sempre compilen, el catàleg de seqüències — switch a Strategy/State, condicional de creació a Factory, telescòpic a Builder, classe déu a Facade, clons de format a Template Method — i el criteri econòmic del deute tècnic per triar on sí i on no. Fowler dóna els moviments, Kerievsky les destinacions, i el mètode de 05-01 el diagnòstic previ. Però aquest camí té direcció de tornada: a vegades el problema no és arribar al patró sinó que algú hi va arribar de més — patrons plantats sense símptoma, Singletons que amaguen estat global, fàbriques d'una sola cosa. Reconèixer aquests excessos, i saber desfer-los, tanca el mòdul: Antipatrons: Quan els Patrons es Tornen un Problema.

Curs de Patrons de Disseny de Programari

Mòdul 1: Introducció als Patrons de Disseny

Mòdul 2: Patrons Creacionals

Mòdul 3: Patrons Estructurals

Mòdul 4: Patrons de Comportament

Mòdul 5: Aplicació de Patrons de Disseny

Mòdul 6: Patrons de Disseny Avançats

Mòdul 7: Recursos Addicionals i Conclusió

© Copyright 2026. Tots els drets reservats