Un curs de patrons honest ha d'acabar aquí: al seu costat fosc. Si un patró és una solució provada a un problema recurrent, un antipatró és el contrari i alguna cosa més verinosa — una solució atractiva i recurrent que empitjora el problema, i que sovint arriba vestida de bona pràctica. Els patrons del catàleg poden convertir-se en antipatrons quan s'apliquen sense símptoma, i aquest mòdul sencer n'ha construït les defenses: el mètode de 05-01, els descarts de 05-02, la disciplina de 05-04. Aquesta lliçó les completa amb la galeria dels clàssics, els mals usos concrets del que has après al curs, els senyals d'alarma per detectar-los en revisió de codi, i l'operació inversa: desfer un patró mal aplicat.

Contingut

  1. Què és un antipatró
  2. La galeria clàssica: símptoma, causa, remei
  3. Mals usos dels patrons del curs
  4. Senyals d'alarma en revisió de codi
  5. Des-refactoritzar: desfer un patró mal aplicat
  6. Exercicis i conclusió
  7. Tancament del mòdul

Què és un antipatró

El terme el va popularitzar el llibre AntiPatterns (Brown, Malveau, McCormick, Mowbray, 1998), i la seva definició té dues parts obligatòries:

  1. Una solució recurrent que sembla bona i produeix conseqüències negatives — no n'hi ha prou amb "codi dolent": l'antipatró sedueix, per això es repeteix.
  2. Un remei documentat — igual que un patró té forma problema→solució, l'antipatró té forma trampa→sortida.

La segona part és la que el fa útil en equip: anomenar l'antipatró ("això és un Golden Hammer") no és un insult, és un diagnòstic amb tractament adjunt — el mateix vocabulari compartit de 01-01, aplicat al costat dolent del mapa.

La galeria clàssica

Antipatró Símptoma Causa típica Remei
God Object (objecte déu) Una classe enorme que sap i fa de tot; tot canvi hi passa; impossible de testar aïllada Afegir "només un mètode més" durant anys; por de crear classes Extract Class per responsabilitats; deixar com a molt una Facade que orquestra (la seqüència de 05-04)
Spaghetti Code Flux impossible de seguir: salts, flags, mètodes quilomètrics, tot depèn de tot Créixer sense disseny ni refactorització; pressa crònica Caracteritzar amb tests, extreure mètodes/classes, introduir costures; patrons només després, sobre el codi ja llegible
Golden Hammer (martell d'or) La mateixa solució per a tot problema ("tot és un microservei", "tot porta Observer") Dominar una sola eina; èxit previ extrapolat Diagnòstic abans que solució (05-01); aprendre l'eina veïna; revisió creuada
Lava Flow (flux de lava) Codi mort o incomprensible que ningú no esborra "per si de cas"; capes fòssils d'intents antics Por d'esborrar sense tests; pèrdua del context històric Cobertura + esborrat valent (el VCS recorda); eliminar flags i branques mortes a cada pas per la zona
Poltergeist Classes efímeres que només creen o invoquen altres i desapareixen; indirecció sense aportació "Més classes = més OO"; capes de gestió ritual Inline Class: fusionar el fantasma amb qui fa la feina real
Copy-Paste Programming El mateix bloc, amb variacions subtils, en N llocs; els bugs s'arreglen en N−1 És més ràpid copiar que abstreure... la primera setmana Regla de tres → extreure mètode/classe; si les còpies varien per un eix, aquest eix demana el seu patró (Strategy, Template Method)
Magic Numbers / Strings Literals sense nom governant la lògica (if (estat == 3)) Pressa; "ja ho documentaré" Constants amb nom, enums; si el literal selecciona conducta, potser State/Strategy
Reinventing the Wheel Framework casolà de logging/esdeveniments/injecció convivint amb l'estàndard Desconeixement de l'ecosistema; síndrome "aquí som especials" Usar la peça estàndard (05-03 va ensenyar a reconèixer-les); el codi propi, només per al domini propi

Fixa't en l'asimetria amb el catàleg GoF: els patrons es trien; els antipatrons es contreuen, com els hàbits. Per això el remei gairebé sempre inclou un canvi de procés (tests, revisió, regla de tres) i no només un canvi de codi.

Mals usos dels patrons del curs

La part que ens toca de prop: cada família del catàleg té la seva manera característica d'aplicar-se malament. Els cinc casos que més veureu:

Singletonitis: estat global disfressat

El Singleton és el patró més fàcil d'escriure i el més car de mantenir, i ja ho vam advertir a 02-02. La malaltia no és tenir un Singleton: és que getInstance() es converteixi en la manera normal d'obtenir dependències. Símptomes: tests que es contaminen entre si (estat compartit que sobreviu), impossibilitat de tenir dues configuracions (recordeu ConfiguracioPideYa quan vam voler testar el mercat de Mèxic?), dependències invisibles — la signatura del mètode menteix sobre allò que usa. Remei: injecció de dependències — la unicitat la gestiona qui compon (el contenidor, el main), no la classe; exactament el que Spring fa amb el seu singleton-per-contenidor (05-03).

Patternitis: la sobreenginyeria amb pedigrí

El cas fundacional del curs: l'email de benvinguda "catedral" de 01-06AbstractEmailSenderFactory, estratègia de salutació, template de plantilles... per a un email que feia dos anys que no canviava. La patternitis és el Golden Hammer de qui ha estudiat el catàleg: cada problema rep el patró, encara que no hi hagi símptoma. Es reconeix perquè les abstraccions tenen una sola raó imaginària d'existir ("per si algun dia...") i perquè el nombre de fitxers que cal obrir per entendre una funció creix sense que creixi el que la funció fa. Remei: els cinc filtres de la balança, i la humilitat de l'"espera fins que faci mal". Els patrons es guanyen, no es planten — tercera vegada que ho escrivim al curs, perquè és la lliçó que més s'oblida.

La Factory d'una sola implementació

// El trio cerimonial complet, vist en tants projectes reals:
public interface ServeiComandes { ... }
public class ServeiComandesImpl implements ServeiComandes { ... }     // la UNICA implementacio
public class ServeiComandesFactory {
    public static ServeiComandes crear() {
        return new ServeiComandesImpl();                              // ...i sempre aquesta
    }
}

Tres fitxers on en bastava un. La interfície-mirall amb el seu Impl i la seva fàbrica només paguen quan hi ha (o hi ha evidència imminent de) una segona implementació, un proxy a interposar o una frontera de mòdul real a protegir — si no, és indirecció ritual: cada lectura salta per tres fitxers per no guanyar-hi res. Remei: Inline — classe concreta i new (o injecció directa), i la interfície s'extreu en un minut amb l'IDE el dia que arribi la segona implementació (05-04). Que l'extracció futura sigui barata és precisament el que fa innecessària l'extracció especulativa.

Jerarquies Visitor prematures

El Visitor va ser el patró amb la lletra petita més llarga del mòdul 4: doble despatx, un mètode per tipus de node a cada visitant, i la jerarquia de nodes congelada (afegir un node obliga a tocar tots els visitants). Muntar-lo "perquè la carta segurament tindrà moltes operacions" quan hi ha una sola operació — i la jerarquia de nodes encara està creixent — és comprar el cost exacte en el pitjor moment: pagues la rigidesa sobre l'eix que encara es mou. Remei: mentre les operacions siguin una o dues i els nodes canviïn, mètodes als nodes o instanceof amb pattern matching (Java 21 el va deixar digne); Visitor quan l'eix s'inverteixi de debò: nodes estables, operacions brotant.

El mediador déu

La degeneració anunciada a 04-06: CentralRepartiment neix per desacoblar col·legues i, protocol a protocol, absorbeix la lògica de negoci de tots ells — 900 línies on cuina, repartidors i comandes són titelles sense conducta pròpia. És el God Object amb document d'identitat de patró, i per això és més perillós: sobreviu a les revisions ("és un Mediator, és al llibre"). Senyal de tall: el mediador ha de coordinar converses, no executar feines — si té regles que pertanyen conceptualment a un col·lega, torneu-les-hi; si els seus criteris varien, extraieu-los cap a estratègies injectades (com l'EstrategiaAssignacio).

Senyals d'alarma en revisió de codi

La checklist per al revisor — cada senyal és una pregunta a fer, no un veredicte automàtic:

  • El nom del patró sense la seva intenció: un *Factory amb una sola cosa a fabricar, un *Manager/*Helper que no sap dir què gestiona, un Observer amb exactament un observador previst per sempre.
  • Abstraccions amb població u: interfície amb una implementació, jerarquia amb una fulla, paràmetre d'estratègia que sempre rep la mateixa. Pregunta: "quina és la segona variant, i quan arriba?"
  • Indirecció sense duana: si en seguir una crida travesses tres classes que només deleguen sense afegir decisió, validació ni traducció — fa olor de Poltergeist amb vocabulari GoF.
  • El diff desproporcionat: la feature era "afegir un camp al tiquet" i el PR porta dues interfícies, una fàbrica i un builder. Pregunta: "quin símptoma actual paga aquesta estructura?"
  • Tests que es barallen amb el disseny: si testar obliga a resetejar singletons, a mocks de mocks, o a arrencar mig sistema — el disseny està cridant; els tests són el primer client del codi i el millor detector d'antipatrons.
  • La justificació en futur: "això ens permetrà...", "quan escalem...". El codi es justifica per símptomes en present; el futur s'apunta en una nota d'intenció, no en classes.
  • Por localitzada: "això millor no ho toquis" assenyalant una classe. On hi ha por hi ha God Object, Lava Flow o falta de tests — i probablement les tres coses.

Des-refactoritzar: desfer un patró mal aplicat

L'operació inversa a 05-04, amb la mateixa disciplina exacta — tests primer, passos que compilen, commits petits — i els moviments en mirall: on abans extrèiem, ara inline.

Patró sobrant Operació inversa
Factory d'una implementació Inline de la fàbrica als punts de crida (new o injecció directa); esborrar la interfície si ningú més no la usa
Strategy amb una sola estratègia Inline del mètode de l'estratègia al context; esborrar interfície i camp
Singleton Introduir l'objecte com a dependència injectada de dalt a baix; getInstance() queda com a adaptador deprecat fins que mori el darrer ús (la migració gradual de 05-04, en mirall)
Visitor prematur Moure cada visitX com a mètode al node corresponent (o a un switch amb pattern matching); esborrar acceptar
Mediador déu Tornar cada regla al col·lega propietari (Move Method); el mediador queda amb l'encaminament pur — que era la seva feina
Decorator/capa cerimonial única Inline de la capa al component; conservar-la només si controla, tradueix o afegeix de debò

Exemple mínim, desfent la fàbrica cerimonial en tres commits:

// Commit 1: la fabrica delega... en res que mereixi existir. Marcar-la:
@Deprecated  // usar new ServeiComandes() o injeccio — veure PR-1841
public class ServeiComandesFactory { ... }

// Commit 2..n: cada punt de crida, migrat i compilant:
ServeiComandes servei = new ServeiComandes(repositori, esdeveniments); // abans: Factory.crear()

// Commit final: esborrar ServeiComandesFactory i fusionar interficie + Impl
// (Rename de ServeiComandesImpl → ServeiComandes amb l'IDE: zero risc).

La regla psicològica importa tant com la tècnica: desfer un patró no és admetre un error vergonyós — és la mateixa enginyeria que el va posar, aplicada a informació nova (l'eix de variació imaginat no va arribar mai). Un equip que sap des-refactoritzar aplica patrons amb menys por, perquè cap decisió no és una cadena perpètua.

Errors Comuns i Consells

  • Usar "antipatró" com a arma llancívola en revisió. El diagnòstic inclou remei i context o no és diagnòstic; "això és Singletonitis, proposo injectar X i Y" construeix — "això és un antipatró" a seques, no.
  • Sobrecorregir: passar de la patternitis a l'"aquí no s'usa cap patró" és el mateix Golden Hammer amb el martell contrari. El criteri continua sent el símptoma, en totes dues direccions.
  • Confondre antipatró amb context diferent: un Singleton en un script de migració d'una tarda no és Singletonitis; un switch de tres casos estables no demana Strategy. Els remeis s'apliquen on hi ha símptoma, no on hi ha semblança.
  • Des-refactoritzar sense xarxa: treure estructura trenca exactament igual que posar-la. Tests de caracterització també aquí.
  • Consell: als PRs que introdueixin un patró, demaneu una línia de justificació amb el símptoma ("segon tipus de contracte, tercer if duplicat"). Costa deu segons i filtra el 90% de la patternitis abans de néixer.
  • Consell: mantingueu una petita "galeria local" d'antipatrons del propi projecte (amb enllaços als PRs que els van desfer): ensenya més que qualsevol llibre, perquè els exemples són de casa.

Exercicis

Exercici 1: diagnòstic a la galeria

Classifica cada cas amb el seu antipatró (clàssic o dels cinc del curs) i esbossa el remei en una frase:

  1. UtilitatsComanda té 74 mètodes estàtics; apareix importada en 180 fitxers i hi ha tres versions de calcularTotal amb resultats lleugerament diferents.
  2. Per llegir una propietat de configuració: ConfigReaderFactory.getInstance().createReader().getConfig().getValue("timeout") — quatre classes, cap amb lògica.
  3. L'equip va resoldre bé les notificacions amb Bridge al mòdul 3; des de llavors, les tres darreres features han entrat com "un Bridge nou", inclosa una on només hi havia una dimensió.
  4. A GestorDescomptes hi ha un bloc comentat de 200 línies amb la nota "// sistema antic de cupons — NO ESBORRAR (s'usa encara a Mexic?)", del 2024.

Exercici 2: la revisió del PR

Arriba un PR titulat "Preparar l'exportació d'informes per al futur": afegeix ExportadorInforme (interfície), ExportadorInformeCsvImpl (única implementació), FabricaExportadors (retorna sempre l'anterior) i GestorExportacio (crida la fàbrica i delega). L'únic requisit de l'sprint era "exportar l'informe de tancament a CSV". Escriu (a) els senyals d'alarma concrets de la checklist que hi apliquen, (b) el comentari de revisió que hi deixaries — constructiu, amb remei i amb el criteri de quan SÍ que valdria la pena aquesta estructura.

Exercici 3: pla de des-refactorització

ConfiguracioPideYa continua sent un Singleton clàssic (getInstance()) usat des de 23 classes, i els tests d'integració es trepitgen la configuració els uns als altres. Dissenya el pla de des-refactorització gradual: ordre dels passos, com conviuen getInstance() i la injecció durant la migració, quina xarxa de seguretat uses i quina és la condició d'esborrat final.

Solucions

Solució 1: (1) God Object (en la seva variant "classe d'utilitats") amb Copy-Paste a dins (tres calcularTotal): extreure per responsabilitats cap als objectes de domini propietaris de cada càlcul, unificar els clons amb tests que fixin quin dels tres comportaments és el correcte. (2) Poltergeist en cadena: inline de les capes fantasma fins a deixar configuracio.timeout() — una peça amb lògica real (llegir i desar en memòria cau) i cap de cerimonial. (3) Golden Hammer postèxit: tornar al diagnòstic per forces (05-01) — Bridge paga amb dues dimensions que varien independentment; amb una, és una interfície normal. (4) Lava Flow: resoldre la incògnita (Mèxic l'usa? — els logs o l'equip de Mèxic ho saben en una hora), i esborrar; el VCS és la xarxa, el comentari-mòmia no ho és.

Solució 2: (a) Abstraccions amb població u (interfície, fàbrica d'un producte); justificació en futur ("per al futur" al mateix títol); diff desproporcionat (quatre classes per a un requisit d'una); Poltergeist probable a GestorExportacio (només crida i delega). (b) Comentari tipus: «El requisit es cobreix amb una classe ExportadorInformeCsv i el seu test. La interfície i la fàbrica no tenen avui segona variant que les justifiqui — proposo deixar-les fora i anotar la intenció: quan arribi el segon format (és al roadmap?), extreure ExportadorInforme amb l'IDE costa un minut i llavors la fàbrica —o el Template Method de l'informe de tancament, que ja existeix— serà l'estructura correcta amb dos casos reals al davant. Així qui vingui llegeix una classe, no quatre.» — anomena el símptoma absent, dóna el remei, fixa el criteri de reobertura i no humilia ningú.

Solució 3: Xarxa: tests de caracterització dels consums de configuració més delicats + els tests d'integració existents (que a més són la motivació: deixaran de trepitjar-se). Passos: 1. fer injectable la classe (constructor públic o de factoria, la instància deixa d'autogestionar-se) mantenint getInstance() com a pont que retorna una instància per defecte — verd, commit; 2. migrar les 23 classes per tandes: cadascuna passa a rebre ConfiguracioPideYa per constructor (les tandes les marca el graf: primer les fulles, després qui les crea) — commit per tanda; 3. els tests d'integració ja construeixen la seva pròpia configuració per escenari — la contaminació desapareix i ho prova; 4. @Deprecated getInstance() quan només quedin usos interns; 5. condició d'esborrat: zero crides a getInstance() fora del punt de composició (el main/contenidor, únic lloc que decideix la unicitat a partir d'ara). És la migració gradual de 05-04 en mirall: construir la via nova, migrar per tandes en verd, enderrocar la vella al final.

Conclusió

Ja tens el mapa del costat fosc: la definició honesta d'antipatró (solució seductora + conseqüències + remei), la galeria clàssica del God Object al Lava Flow, i els cinc mals usos del catàleg que aquest mateix curs podria provocar — Singletonitis, patternitis, fàbriques cerimonials, Visitors prematurs i mediadors déu — amb els seus senyals d'alarma per caçar-los en revisió i la disciplina mirall per desfer-los sense por. La simetria final del mòdul és aquesta: aplicar un patró i retirar-lo són la mateixa enginyeria, guiada pel mateix jutge — el símptoma present, mai la moda ni la por.

Tancament del mòdul

Amb això, el mòdul de l'ofici queda complet: saps triar patró amb mètode i descarts explícits, has vist el catàleg col·laborant a escala real a PideYa, el reconeixes al JDK, a Spring i al codi aliè, saps refactoritzar cap a un patró amb xarxa de seguretat — i ara també **des d'**ell quan sobra. El catàleg GoF ja no és una llista de 23 fitxes: és una caixa d'eines amb criteri d'ús, que era la promesa del curs.

Però el GoF es va escriure per a objectes dins d'un procés, i el programari del 2026 viu repartit: serveis que es criden per xarxa, fils que competeixen per dades, arquitectures que imposen els seus propis patrons — hexagonal, CQRS, sagues, circuit breakers, productors i consumidors. Molts et resultaran familiars, perquè són les mateixes intencions del catàleg estirades sobre la xarxa i la concurrència; d'altres són fauna nova amb problemes nous. Aquest és el territori del mòdul que comença: ens veiem a Patrons de Disseny en Arquitectures Modernes.

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