Si aquest curs només t'ensenyés els 23 patrons, et faria un mal favor: en sortiries amb un martell nou i tot et semblaria un clau. El desenvolupador que acaba d'aprendre patrons i els aplica a tot arreu és un clixé tan real que té nom propi a la professió ("patternitis"). Aquesta última lliçó del mòdul introdueix el contrapès: què hi guanyen de debò els equips que fan servir patrons, què hi paguen, i —el més valuós— amb quins criteris decidir en cada cas si un patró val la pena. Amb aquesta balança ben calibrada estaràs llest per estudiar els patrons un a un sense perdre el judici crític.
Contingut
- Els avantatges reals dels patrons
- Els costos i riscos
- El risc més gran: la sobreenginyeria
- Criteris per decidir si aplicar un patró
- Taula resum de la balança
- Com estudiar els propers mòduls amb aquesta balança al cap
Els avantatges reals dels patrons
Vocabulari comú: l'avantatge més subestimat
El guany més immediat no és al codi sinó a la comunicació. Compara aquestes dues frases en una reunió de l'equip de PideYa:
«He fet una interfície amb un mètode de càlcul, i després diverses classes que la implementen, una per cada manera de calcular l'enviament, i la classe de la comanda en rep una i la crida sense saber quina és...»
«El càlcul de l'enviament és una Strategy.»
La segona frase transmet en cinc paraules l'estructura completa, les responsabilitats i fins i tot les conseqüències esperables, perquè tots dos interlocutors comparteixen el catàleg. Aquest vocabulari opera a tot arreu: revisions de codi, documentació, noms de classes (ComandaBuilder, NotificadorObserver), entrevistes tècniques i la mateixa documentació de les llibreries que fas servir.
Solucions provades: no pagar la novatada
Un patró condensa dècades d'assaig i error de milers d'equips. Quan a PideYa calgui notificar canvis de comanda a parts interessades (el problema de la lliçó 1), no partirem de zero: la solució catalogada ja té resolts els problemes de segon ordre que tu encara no has vist venir (què passa si un subscriptor falla? I si es dona de baixa durant la notificació?). Les seccions d'"Implementació" i "Conseqüències" d'un patró són emboscades ja desactivades.
Mantenibilitat i evolució
Els patrons apliquen els principis de disseny de forma sistemàtica, i això es tradueix en codi que absorbeix canvis: afegir una passarel·la de pagament, un canal de notificació o una promoció sense tocar el que funciona (OCP), i en codi testejable, perquè les abstraccions que introdueixen els patrons són exactament els punts on injectar dobles de prova (DIP).
Llegibilitat per a qui arriba després
Un disseny amb patrons ben aplicats és un disseny autodocumentat per a qualsevol desenvolupador format: qui obre el projecte i veu un CistellaMemento o un AdaptadorPassarellaStripe sap què esperar abans de llegir una línia del cos. Es redueix el cost d'incorporació a l'equip i el risc de "només en Joan entén aquesta part".
Pont cap a frameworks i plataformes
Com vam veure a la lliçó d'història, els frameworks estan construïts amb patrons. Conèixer-los converteix la "màgia" de Spring, JPA o Android en mecanismes recognoscibles, i et permet estendre aquests frameworks pels punts d'extensió que els seus autors van dissenyar (que són, gairebé sempre, patrons exposats).
Els costos i riscos
Cap patró no és gratuït. Els costos són reals i convé mirar-los de cara:
Complexitat accidental i indirecció
Tot patró afegeix peces: interfícies, classes petites, salts entre fitxers. On hi havia un mètode amb tres if, pot acabar havent-hi una interfície, quatre implementacions i una fàbrica. Si la flexibilitat que compren aquestes peces es fa servir, és un bon negoci; si no, has canviat tres if llegibles per set fitxers que cal obrir en ordre per entendre què passa. Aquesta complexitat que no ve del problema sinó de la solució s'anomena complexitat accidental, i és l'impost bàsic dels patrons.
Corba d'aprenentatge i barrera d'entrada
El vocabulari comú només funciona si tot l'equip el parla. En un equip on la meitat no coneix els patrons, un disseny ple d'ells no comunica: intimida. El codi amb patrons és més llegible per a qui els coneix i menys per a qui no; això és un cost organitzatiu real que has de considerar en decidir quant patró introdueix el teu disseny.
Aplicar un patró on no toca
El risc més nociu no és implementar malament un patró, sinó implementar bé el patró equivocat (o un d'innecessari). Símptomes típics:
- Triar el patró per la solució ("em ve de gust usar un Builder") en lloc de pel problema.
- Forçar el problema perquè encaixi en el patró, en lloc del contrari.
- Introduir la flexibilitat d'un patró en un eix on el canvi no arriba mai (violació de YAGNI amb disfressa elegant).
Quan aquests errors es sistematitzen, cristal·litzen en antipatrons: solucions recurrents que semblen bones i són nocives. Els dedicarem una lliçó completa al mòdul 5 (Antipatrons); de moment, queda't que existeixen i que diversos neixen de patrons mal aplicats.
Cost de rendiment (menor, però existeix)
La indirecció té un cost d'execució (crides virtuals, objectes extra, memòria). En el 99% del codi d'una aplicació com PideYa és menyspreable davant del cost d'una consulta a base de dades; només en punts calents extrems arriba a importar. És el menys important dels costos, però citar-lo és honest.
El risc més gran: la sobreenginyeria
Mereix secció pròpia perquè és la malaltia professional de qui acaba d'aprendre patrons. Sobreenginyeria és construir més estructura de la que el problema necessita. Vegem-la a PideYa:
El problema real: PideYa necessita enviar un email de benvinguda quan es registra un client. Un, senzill, sempre igual.
La solució sobreenginyeritzada (no imitis això):
// Interficie per a "flexibilitat futura"
public interface EstrategiaBenvinguda { void executar(Client c); }
// Fabrica abstracta d'estrategies "per si hi ha mes canals"
public interface FabricaBenvingudes { EstrategiaBenvinguda crear(); }
// Registre amb instancia unica de la fabrica de fabriques...
public class RegistreFabriques { /* ... */ }
// ...i la unica implementacio real, amagada al fons:
public class BenvingudaEmail implements EstrategiaBenvinguda {
public void executar(Client c) { /* enviar l'email */ }
}La solució proporcionada:
public class ServeiRegistre {
private final ServeiEmail email; // injectat: testejable (DIP)
public ServeiRegistre(ServeiEmail email) { this.email = email; }
public void registrar(Client client) {
// ...alta del client...
email.enviarBenvinguda(client);
}
}La segona versió respecta DIP (dependència injectada, testejable) sense muntar cap catedral. Si algun dia màrqueting demana benvingudes per SMS i email i push configurables per país... aquell dia hi haurà un problema real que justifiqui més estructura, i refactoritzar cap a ella serà senzill precisament perquè el codi és simple. La flexibilitat especulativa mai no surt gratuïta i gairebé mai no encerta l'eix del canvi futur.
Regla d'or: els patrons es guanyen, no es planten. S'hi arriba quan el problema estreny, no "per si de cas".
Criteris per decidir si aplicar un patró
Davant de la temptació d'aplicar un patró, passa-la per aquests filtres, en ordre:
- Puc anomenar el problema sense anomenar el patró? Descriu el problema i les seves forces en una frase ("necessitem afegir promocions sense tocar el càlcul"). Si només saps dir "vull usar X", no tens un problema: tens ganes d'usar X.
- El canvi que el patró facilita és real o especulatiu? Ha passat ja almenys una vegada, és al roadmap, o és una intuïció? Els patrons paguen el seu cost quan l'eix de variació és real (les promocions de PideYa canvien cada mes: real; "potser algun dia suportarem criptomonedes": intuïció).
- L'alternativa simple ja fa mal? Mira el codi actual: hi ha símptomes concrets (duplicació en afegir casos, cadenes d'
ifcreixents, tests impossibles, classes que canvien per motius aliens)? Si el codi simple encara no fa mal, sol ser aviat. - L'equip podrà mantenir-lo? Un disseny que només tu entens és un passiu, no un actiu. Si introdueixes un patró poc conegut, acompanya'l: anomena'l al codi i comenta la intenció.
- El cost queda per sota del benefici? Compta les peces que afegeix (interfícies, classes, salts) i compara-les honestament amb el que compra. En cas d'empat, guanya l'opció simple (KISS).
Un flux de decisió compacte:
flowchart TD
A[Temptació d'aplicar un patró] --> B{Problema anomenable<br/>sense citar el patró?}
B -- No --> Z[No l'apliquis:<br/>és solució a la cerca de problema]
B -- Sí --> C{Eix de canvi real<br/>o que ja fa mal?}
C -- No --> Y[Espera: codi simple<br/>+ nota d'intenció]
C -- Sí --> D{Benefici > cost<br/>i equip preparat?}
D -- No --> Y
D -- Sí --> E[Aplica'l: anomena'l al codi<br/>i documenta la intenció]
Fixa't en la casella "Espera": no aplicar un patró avui no és renunciar-hi. És deixar el codi simple i net perquè, quan el canvi real arribi, refactoritzar cap al patró sigui barat. De fet, el camí més sa cap als patrons és la refactorització guiada per símptomes, i li dedicarem una lliçó sencera al mòdul 5 (Refactorització Usant Patrons).
Taula resum de la balança
| Aspecte | Avantatge | Cost / risc associat |
|---|---|---|
| Comunicació | Vocabulari comú i precís de l'equip | Només funciona si tots coneixen el catàleg |
| Qualitat de la solució | Dècades d'experiència destil·lades; trampes ja resoltes | Falsa seguretat si s'aplica el patró equivocat |
| Mantenibilitat | Canvis localitzats (OCP), codi testejable (DIP) | Més classes i indirecció a mantenir |
| Llegibilitat | Disseny autodocumentat per a qui coneix patrons | Barrera d'entrada per a qui no els coneix |
| Evolució | Punts d'extensió preparats per al canvi real | Sobreenginyeria si el canvi era especulatiu (YAGNI) |
| Relació amb frameworks | Entens i estens millor les eines | — |
| Rendiment | — | Indirecció amb cost marginal (rarament rellevant) |
I la síntesi en una frase: un patró és una inversió: compra flexibilitat i comunicació pagant complexitat; només és rendible si aquesta flexibilitat es fa servir i aquesta comunicació es comparteix.
Com estudiar els propers mòduls amb aquesta balança al cap
A partir del mòdul 2 estudiaràs patrons concrets, un per lliçó. Perquè la balança d'avui no es quedi en teoria, adopta aquesta disciplina en cada patró:
- Llegeix primer el problema a PideYa i intenta resoldre'l tu, de forma simple, abans de veure la solució del patró. Així sentiràs què afegeix el patró i què afegeix la teva solució ingènua.
- En arribar a les conseqüències, no te les saltis: són la meitat del patró. Pregunta't sempre "en quin cas NO l'usaria?". Si no saps respondre, encara no coneixes el patró.
- A les comparatives de final de mòdul, torna als criteris d'aquesta lliçó: són l'àrbitre entre patrons rivals.
Errors Comuns i Consells
- La "patternitis" del convers recent. Després d'aprendre els patrons, tot sembla demanar-ne un. És una fase normal; la vacuna és el filtre núm. 1: si no pots anomenar el problema sense anomenar el patró, no hi ha problema.
- Mesurar la qualitat d'un disseny per quants patrons té. La mètrica és exactament la contrària: el millor disseny és el més simple que resol el problema i absorbeix els canvis reals. Zero patrons pot ser la resposta correcta.
- Usar el patró com a excusa per no pensar. "Aquí hi va un Singleton perquè sempre es fa així" no és disseny, és litúrgia. Cada aplicació d'un patró s'ha de poder defensar amb el problema i les forces concretes del cas.
- No anomenar els patrons al codi. Si apliques un patró, digues-ho:
ComandaBuildercomunica;GestorComandes2amaga. L'avantatge del vocabulari es perd si el patró queda camuflat. - Descartar els patrons per por de la sobreenginyeria. El pèndol contrari també és un error: codi sense cap abstracció, amb duplicació i
ifen cadena, és tan car com el sobredissenyat. La virtut no és "sense patrons" sinó "els patrons justos". - Consell: al teu proper disseny, escriu en un comentari o a la descripció del pull request quin canvi futur concret justifica cada abstracció que introdueixes. Si no pots escriure'l, probablement sobra.
Exercicis
Exercici 1: passar un cas pels filtres
L'equip de PideYa debat introduir una jerarquia d'estratègies intercanviables per al càlcul de l'IVA de les comandes. Dades: PideYa opera només a Espanya, l'IVA del menjar a domicili no ha canviat en anys, i no hi ha plans d'expansió internacional al roadmap. Aplica els cinc criteris de la secció 4 i emet un veredicte raonat.
Exercici 2: detectar la sobreenginyeria
Assenyala quins elements d'aquest disseny per a l'avís "comanda en repartiment" són sobreenginyeria, sabent que l'únic requisit és enviar una notificació push al client, i proposa la versió proporcionada:
public interface CanalAvis { void avisar(Client c, String msg); }
public interface FabricaCanals { CanalAvis crearCanal(String tipus); }
public class FabricaCanalsImpl implements FabricaCanals { /* switch d'un cas */ }
public class GestorCanals { /* mante un registre de fabriques de canals */ }
public class CanalPush implements CanalAvis { /* l'unic canal existent */ }Exercici 3: argumentar la balança
Escriu dos paràgrafs breus: un defensant davant del teu equip l'ús d'un patró per a les promocions de PideYa (canvien cada mes, les escriu més d'un desenvolupador), i un altre defensant NO usar cap patró per a l'email de benvinguda (un de sol, estable des de fa dos anys). En cada paràgraf cita almenys un avantatge o cost concret d'aquesta lliçó.
Solucions
Solució 1: (1) El problema és anomenable: "calcular l'IVA segons regles que podrien variar"; passa el primer filtre. (2) L'eix de canvi és especulatiu: un sol país, tipus estable durant anys, sense expansió al roadmap: falla clarament. (3) L'alternativa simple (una constant o un mètode calcularIva(total)) no fa gens de mal avui. (4–5) Ja irrellevants, però el cost (interfície + implementacions + injecció) superaria un benefici inexistent. Veredicte: no aplicar el patró; deixar el càlcul en un únic punt ben anomenat (això sí que ho exigeix DRY) perquè, si algun dia s'internacionalitza la plataforma, la refactorització sigui local i barata.
Solució 2: sobren FabricaCanals, FabricaCanalsImpl (una fàbrica amb un switch d'un sol cas) i GestorCanals (un registre de fàbriques per a una fàbrica d'un canal): tres nivells d'indirecció sense cap variació real a gestionar. És defensable conservar la interfície CanalAvis amb la seva única implementació CanalPush injectada on es faci servir (cost mínim, facilita el test), tot i que fins i tot ella és prescindible. Versió proporcionada:
public class ServeiAvisos {
private final CanalAvis canal; // avui, sempre CanalPush
public ServeiAvisos(CanalAvis canal) { this.canal = canal; }
public void avisarEnRepartiment(Comanda comanda) {
canal.avisar(comanda.getClient(), "La teva comanda esta en repartiment!");
}
}Si demà apareixen SMS o email com a requisit real, afegir implementacions de CanalAvis serà trivial; les fàbriques s'introduiran només si la selecció del canal es converteix en un problema en si mateixa.
Solució 3 (redaccions possibles):
A favor (promocions): «Les promocions canvien cada mes i les toca gent diferent: l'eix de variació és real i freqüent. Encapsular cada promoció darrere d'una abstracció comuna ens dona canvis localitzats —afegir una promoció serà crear una classe, sense tocar ni tornar a testejar les existents (OCP)— i un vocabulari compartit: a les revisions direm "és una promoció nova" i tots sabrem quina estructura esperar. El cost en classes extra s'amortitza el primer mes.»
En contra (benvinguda): «L'email de benvinguda és únic i porta dos anys sense canviar: no hi ha eix de variació que justifiqui indirecció. Muntar-hi abstraccions seria flexibilitat especulativa (YAGNI) i complexitat accidental: més fitxers a obrir per entendre un enviament d'email. Mantinguem-lo com una crida directa, testejable per injecció del servei d'email; si algun dia el requisit creix, refactoritzar des de codi simple serà més barat que mantenir anys una catedral buida.»
Conclusió
Amb aquesta lliçó es tanca el mòdul introductori, i amb ell ja disposes de tot l'equip de base: saps què és un patró (context, problema, solució, conseqüències) i què no ho és, d'on ve la disciplina (d'Alexander al GoF i més enllà), quins principis encarnen els patrons (SOLID, DRY, KISS, YAGNI i les dues màximes del GoF), com llegir els seus diagrames (UML amb mermaid), com s'organitza el catàleg (creacionals, estructurals i de comportament) i —el que has après avui— com sospesar els seus avantatges contra els seus costos per aplicar-los amb criteri i no per moda.
És hora de passar dels fonaments al primer pis de l'edifici. Al mòdul 2 abordarem la primera família del catàleg, la que governa el naixement dels objectes: com crear les peces de PideYa —passarel·les de pagament, notificadors, comandes complexes— sense acoblar el codi a les seves classes concretes. Ens veiem a la Introducció als Patrons Creacionals.
Curs de Patrons de Disseny de Programari
Mòdul 1: Introducció als Patrons de Disseny
- Què són els Patrons de Disseny?
- Història i Origen dels Patrons de Disseny
- Principis de Disseny: SOLID i Altres Fonaments
- UML Essencial per Entendre Patrons
- Classificació dels Patrons de Disseny
- Avantatges i Desavantatges d'Usar Patrons de Disseny
Mòdul 2: Patrons Creacionals
- Introducció als Patrons Creacionals
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
- Comparativa i Elecció de Patrons Creacionals
Mòdul 3: Patrons Estructurals
- Introducció als Patrons Estructurals
- Adapter
- Bridge
- Composite
- Decorator
- Facade
- Flyweight
- Proxy
- Comparativa i Elecció de Patrons Estructurals
Mòdul 4: Patrons de Comportament
- Introducció als Patrons de Comportament
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
- Comparativa i Elecció de Patrons de Comportament
Mòdul 5: Aplicació de Patrons de Disseny
- Com Seleccionar el Patró Adequat
- Exemples Pràctics d'Ús de Patrons
- Patrons de Disseny en Projectes Reals
- Refactorització Usant Patrons de Disseny
- Antipatrons: Quan els Patrons es Tornen un Problema
Mòdul 6: Patrons de Disseny Avançats
- Patrons de Disseny en Arquitectures Modernes
- Patrons de Disseny en Microserveis
- Patrons de Disseny en Sistemes Distribuïts
- Patrons de Concurrència
- Patrons de Disseny en Desenvolupament Àgil
