Quan portes un temps programant amb orientació a objectes, comences a notar que certs problemes es repeteixen una vegada i una altra: necessites que existeixi una sola instància d'alguna cosa, vols crear objectes sense acoblar-te a les seves classes concretes, o necessites que diversos components reaccionin quan alguna cosa canvia. Els patrons de disseny són precisament això: solucions provades, amb nom propi, a problemes recurrents del disseny de programari. En aquesta lliçó aprendràs què és exactament un patró de disseny, quins elements el componen, quines coses no són un patró (una confusió molt habitual) i veuràs un primer problema real que ens servirà de motivació. A més, coneixeràs PideYa, el projecte que ens acompanyarà durant tot el curs.

Contingut

  1. El projecte del curs: PideYa
  2. Definició de patró de disseny
  3. Els quatre elements essencials: context, problema, solució i conseqüències
  4. Què NO és un patró de disseny
  5. Com es descriu un patró: la fitxa d'un patró
  6. Un primer cop d'ull motivador: un problema real a PideYa

El projecte del curs: PideYa

Al llarg de tot el curs treballarem sobre un mateix projecte: PideYa, una plataforma fictícia de comandes de menjar a domicili. La idea és que no aprenguis els patrons amb exemples abstractes de ClasseA i ClasseB, sinó aplicant-los a un domini realista que anirem fent créixer mòdul a mòdul.

PideYa té els ingredients típics d'una aplicació d'aquesta mena:

  • Restaurants amb la seva carta de plats, preus i horaris.
  • Clients que naveguen per les cartes, munten una cistella i fan comandes.
  • Pagaments a través de diferents passarel·les (targeta, PayPal, moneder intern...).
  • Repartidors que recullen i lliuren les comandes.
  • Notificacions a clients i repartidors (email, SMS, push) a mesura que avança la comanda.

Aquest domini és una mina d'or per als patrons de disseny: hi ha objectes complexos per construir (una comanda amb les seves línies, descomptes i adreça de lliurament), famílies d'objectes intercanviables (passarel·les de pagament, canals de notificació), estats que canvien (una comanda passa de rebuda a en preparació, en repartiment i lliurada), i components que s'han d'assabentar del que fan altres sense acoblar-s'hi. Cada vegada que un exemple ho permeti, el plantejarem sobre PideYa.

flowchart LR
    C[Client] -->|munta cistella| P[Comanda]
    P -->|paga amb| PG[Passarel·la de pagament]
    P -->|s'assigna a| R[Repartidor]
    P -->|genera| N[Notificacions]
    Rest[Restaurant] -->|prepara| P

No cal que memoritzis res de PideYa ara: anirem presentant cada peça quan la necessitem.

Definició de patró de disseny

Una definició pràctica i àmpliament acceptada és la següent:

Un patró de disseny és una solució general, reutilitzable i amb nom propi a un problema de disseny que apareix de forma recurrent en un context determinat del desenvolupament de programari orientat a objectes.

Desglossem aquesta definició, perquè cada paraula compta:

  • Solució general: el patró no resol el teu problema concret, sinó la família de problemes a la qual pertany el teu. Per això sempre cal adaptar-lo.
  • Reutilitzable: s'ha aplicat amb èxit moltes vegades, en molts projectes. Un truc que només va funcionar una vegada no és un patró; un patró està destil·lat de l'experiència col·lectiva.
  • Amb nom propi: això és més important del que sembla. Dir "aquí fem servir un Observer" comunica en dues paraules una estructura completa de classes, responsabilitats i col·laboracions. Els patrons creen un vocabulari comú entre desenvolupadors.
  • Problema recurrent: si el problema només apareix al teu projecte, la solució pot ser bona, però no és un patró.
  • En un context determinat: cap patró és bo o dolent en abstracte. Un patró és adequat en el seu context i contraproduent fora d'ell.

Una analogia útil: un patró de disseny és al programari el que una recepta clàssica és a la cuina. La recepta de la truita de patates et diu els ingredients, els passos i els resultats esperats, però cada cuiner l'adapta (amb ceba o sense?). Ningú no "copia i enganxa" una truita: la cuina seguint el patró.

Els quatre elements essencials: context, problema, solució i conseqüències

Tot patró de disseny, sigui quin sigui, s'articula sobre quatre elements. Si en falta algun, no tens un patró complet, tens només un fragment d'idea.

Element Pregunta que respon Exemple informal
Context En quina situació apareix el problema? "Una aplicació amb diversos canals de notificació que poden créixer en el futur"
Problema Quin conflicte o força en tensió cal resoldre? "El codi que envia notificacions no hauria de canviar cada vegada que afegim un canal nou"
Solució Quina estructura de classes/objectes i col·laboracions ho resol? "Definir una abstracció comuna i fer que l'emissor depengui només d'ella"
Conseqüències Què hi guanyem i què hi paguem en aplicar-la? "Guanyem extensibilitat; paguem amb més classes i indirecció"

Val la pena aturar-se en dos d'ells:

Les «forces» del problema

El problema d'un patró mai no és trivial del tipus "vull sumar dos nombres". Sempre hi ha forces en tensió: requisits que estiren en direccions oposades. Per exemple: vull flexibilitat per canviar el comportament en temps d'execució (estira cap a més indirecció) davant de vull un codi simple i directe (estira cap a menys classes). El patró és un equilibri documentat entre aquestes forces.

Les conseqüències: no hi ha patró gratuït

Aquest punt separa el professional de l'aficionat: tot patró té un cost. Més classes, més indirecció, més conceptes que el següent desenvolupador ha d'entendre. Un patró ben aplicat és aquell el benefici del qual supera clarament el seu cost en aquell context. Dedicarem la lliçó Avantatges i Desavantatges d'Usar Patrons de Disseny a analitzar-ho en detall.

Què NO és un patró de disseny

Aquí es concentren els malentesos més freqüents. Aclarim els tres principals:

Un patró NO és codi copiable

No existeix "el codi del patró Observer" que puguis enganxar al teu projecte. Un patró descriu una estructura i unes responsabilitats; el codi concret l'escrius tu cada vegada, adaptat al teu domini, el teu llenguatge i les teves restriccions. Dues implementacions correctes del mateix patró poden no compartir ni una sola línia idèntica. Quan en aquest curs vegis codi Java d'un patró, entén-lo com una materialització possible, no com la implementació oficial.

Un patró NO és un framework ni una llibreria

Un framework (Spring, Hibernate...) és programari concret que executes; un patró és coneixement que apliques. La relació real és la inversa: els frameworks estan construïts amb patrons. Spring, per exemple, en fa servir internament desenes, i per això entendre patrons et fa molt millor usuari de frameworks: deixes de veure "màgia" i comences a veure estructures conegudes.

Patró de disseny Framework / llibreria
Naturalesa Coneixement, descripció Codi executable
Com es fa servir S'adapta i s'implementa cada vegada S'importa i s'invoca
Dependència Cap: viu al teu cap i al teu disseny El teu projecte en depèn
Nivell Disseny (microarquitectura de classes) Implementació

Un patró NO és un algorisme

Un algorisme (ordenació ràpida, Dijkstra...) resol un problema computacional: donada una entrada, produeix una sortida en certs passos, i la seva correcció es pot demostrar. Un patró resol un problema d'organització del codi: com repartir responsabilitats entre classes perquè el sistema sigui mantenible i extensible. Un algorisme es mesura en eficiència (temps, memòria); un patró es mesura en qualitats de disseny (acoblament, cohesió, extensibilitat).

Com es descriu un patró: la fitxa d'un patró

Els patrons es documenten en catàlegs, i cada patró es presenta amb una fitxa estandarditzada. L'estructura clàssica (heretada del catàleg del Gang of Four, la història del qual veurem a la lliçó següent) inclou aquestes seccions:

  1. Nom: curt i evocador. És la peça de vocabulari que compartiràs amb el teu equip.
  2. Intenció (intent): una o dues frases amb el que el patró aconsegueix.
  3. També conegut com: altres noms amb què circula.
  4. Motivació: un escenari concret on el problema apareix i el patró el resol.
  5. Aplicabilitat: senyals de quan usar-lo (i, per omissió, quan no).
  6. Estructura: diagrama de classes amb els participants. En aquest curs els dibuixarem amb mermaid, i a la lliçó UML Essencial per Entendre Patrons aprendràs a llegir-los.
  7. Participants: cada classe/interfície del diagrama i la seva responsabilitat.
  8. Col·laboracions: com interactuen els participants en temps d'execució.
  9. Conseqüències: beneficis i costos.
  10. Implementació: consells, variants i trampes en portar-lo a codi.
  11. Codi d'exemple: una materialització en un llenguatge concret.
  12. Usos coneguts: on s'ha aplicat en sistemes reals.
  13. Patrons relacionats: alternatives i combinacions habituals.

En aquest curs farem servir una versió alleugerida d'aquesta fitxa per a cada patró dels mòduls 2, 3 i 4: intenció, problema a PideYa, estructura en mermaid, implementació en Java, conseqüències i patrons relacionats. Acostuma't a aquesta estructura: llegir patrons "en fitxa" és una habilitat en si mateixa, perquè els catàlegs professionals (llibres, wikis d'empresa) fan servir aquest format.

Un primer cop d'ull motivador: un problema real a PideYa

Vegem per què tot això importa, amb un problema real de PideYa. Atenció: aquí només sentirem el dolor del problema; les solucions amb nom i cognoms arribaran a les seves lliçons corresponents.

Quan una comanda canvia d'estat (per exemple, el restaurant la marca com a "en preparació"), cal avisar diverses parts: el client per notificació push, el repartidor assignat, i el panell d'estadístiques internes. Una primera versió ingènua podria ser així:

public class Comanda {

    private String estat;

    public void canviarEstat(String nouEstat) {
        this.estat = nouEstat;

        // Avisar el client per push
        ServeiPush push = new ServeiPush();
        push.enviar(this.getClient().getTokenDispositiu(),
                    "La teva comanda esta: " + nouEstat);

        // Avisar el repartidor per SMS
        ServeiSms sms = new ServeiSms();
        sms.enviar(this.getRepartidor().getTelefon(),
                   "Comanda " + this.getId() + ": " + nouEstat);

        // Actualitzar estadistiques
        PanellEstadistiques panell = new PanellEstadistiques();
        panell.registrarCanviEstat(this.getId(), nouEstat);
    }
}

Expliquem què fa aquest codi i per què, encara que funciona, és un problema esperant a créixer:

  • El mètode canviarEstat actualitza l'estat i, tot seguit, ell mateix crea i crida cada servei interessat: push, SMS i estadístiques.
  • La classe Comanda, que hauria d'ocupar-se de la lògica de negoci d'una comanda, coneix els detalls de tres sistemes que no són cosa seva (com s'envia un push, quin telèfon té el repartidor, que existeix un panell d'estadístiques).

Ara imagina l'evolució típica del producte:

  • Màrqueting demana que també s'enviï un email en certs estats. → Cal tocar Comanda.
  • S'afegeix un programa de fidelització que suma punts en lliurar. → Cal tocar Comanda.
  • Als tests unitaris de Comanda, cada test envia SMS reals (i costen diners!) perquè els serveis es creen amb new dins del mètode. → Difícil de testejar.
  • El repartidor pot no estar assignat encara → NullPointerException en producció.

El problema de fons, en termes de patró, seria:

  • Context: un objecte de negoci els canvis del qual interessen a un nombre variable de tercers.
  • Problema: l'objecte no hauria de conèixer els seus interessats ni canviar cada vegada que n'apareix un de nou; les forces en tensió són notificar tothom davant de no acoblar-se a ningú.
  • Solució: existeix una estructura clàssica que resol exactament això... i té nom. L'estudiarem al mòdul 4 (Observer). També veuràs que la creació d'aquests serveis amb new toca de ple els patrons creacionals del mòdul 2.
  • Conseqüències: les analitzarem allà, perquè tampoc aquesta solució no és gratuïta.

Això és el que et donarà el curs: la capacitat de reconèixer aquestes situacions, posar-los nom i aplicar la solució destil·lada per milers de desenvolupadors abans que tu, coneixent-ne el preu.

Errors Comuns i Consells

  • Creure que aprendre patrons és memoritzar diagrames. L'important és reconèixer el problema i les seves forces; l'estructura es dedueix (o es consulta) després. Si memoritzes la solució sense el problema, aplicaràs patrons on no toquen.
  • Buscar "el codi del patró" per copiar-lo. No existeix. Existeixen exemples, com els d'aquest curs, que has d'adaptar al teu domini. Si copies sense adaptar, acabaràs amb noms genèrics (Manager, Handler) que no diuen res del teu negoci.
  • Confondre patró amb framework. Si algú diu "fem servir el patró Spring", hi ha una confusió de conceptes. Spring fa servir patrons; no és un patró.
  • Voler aplicar un patró com més aviat millor. L'impuls de "posar patrons" tan bon punt s'aprenen és gairebé universal (i perillós). Primer domina el problema que resol cadascun; la lliçó 01-06 i el mòdul 5 et donaran criteris per decidir.
  • Consell: quan llegeixis codi d'altri (o un framework), practica posant nom a les estructures que reconeguis. És el millor entrenament possible i accelera moltíssim la lectura de codi.

Exercicis

Exercici 1: context, problema, solució i conseqüències

Pensa en la situació següent de PideYa i redacta els quatre elements d'un possible patró (sense anomenar cap patró concret, encara que el coneguis): "L'aplicació mòbil de PideYa ha de funcionar amb tres passarel·les de pagament diferents (targeta, PayPal i moneder intern), i l'equip comercial ja està negociant amb una quarta."

Exercici 2: patró o no patró?

Indica, per a cada element, si es pot considerar un patró de disseny i per què sí o per què no:

  1. L'algorisme d'ordenació quicksort.
  2. La llibreria java.util.logging.
  3. "Quan diverses classes comparteixen un comportament que varia, extreure'l a una jerarquia pròpia i injectar-lo, en lloc d'heretar."
  4. Un fitxer Utilitats.java amb mètodes estàtics que el teu equip copia de projecte en projecte.

Exercici 3: detectar les forces

Rellegeix el codi de Comanda.canviarEstat(...) d'aquesta lliçó. Enumera almenys tres "forces" o requisits en tensió que fan que aquest disseny sigui problemàtic, expressant-los com a parells del tipus "volem X, però també volem Y".

Solucions

Solució 1 (una redacció possible):

  • Context: una aplicació de comandes que cobra als seus clients mitjançant proveïdors de pagament externs, el nombre i la varietat dels quals creix amb el negoci.
  • Problema: el codi de cobrament no s'ha de reescriure amb cada passarel·la nova; cada passarel·la té la seva pròpia API, però el procés de comanda hauria de tractar-les de forma uniforme. Forces: uniformitat per a qui cobra davant de diversitat real de les APIs.
  • Solució (esbós): definir una abstracció comuna de "pagament" de la qual depengui el procés de comanda, i encapsular les particularitats de cada proveïdor darrere d'aquesta abstracció, de manera que afegir una passarel·la sigui afegir una peça nova, no modificar les existents.
  • Conseqüències: es guanya extensibilitat i capacitat de provar el cobrament sense passarel·les reals; es paga amb una capa més d'indirecció i amb l'esforç de dissenyar una abstracció que encaixi amb APIs molt diferents.

Solució 2:

  1. No. És un algorisme: resol un problema computacional amb entrada i sortida definides, i s'avalua per eficiència, no per qualitats de disseny.
  2. No. És una llibreria: codi concret que importes i executes. (Internament aplica patrons, però ella mateixa no ho és.)
  3. Sí (o almenys en té la forma): descriu un problema recurrent, una solució general en termes d'estructura i responsabilitats, i és adaptable a molts contextos. De fet, apunta a un principi que veurem a 01-03: afavorir la composició per sobre de l'herència.
  4. No. És codi copiable, just el que un patró no és. Pot ser útil, però no descriu problema, context ni conseqüències: només és una implementació concreta que viatja entre projectes.

Solució 3 (tres exemples vàlids; n'hi ha més):

  • Volem que tots els interessats s'assabentin del canvi d'estat, però també volem que Comanda no en conegui cap.
  • Volem afegir nous avisos (email, fidelització) fàcilment, però també volem no modificar Comanda cada vegada.
  • Volem provar Comanda amb tests unitaris, però també volem que en producció es facin servir els serveis reals d'SMS i push (i amb new dins del mètode, no hi ha manera de substituir-los als tests).

Conclusió

En aquesta lliçó hem establert els fonaments del curs: un patró de disseny és una solució amb nom, provada i adaptable a un problema recurrent, sempre descrita mitjançant quatre elements (context, problema, solució i conseqüències) i documentada en fitxes estandarditzades. Igual d'important és el que un patró no és: ni codi copiable, ni framework, ni algorisme. I amb el problema de les notificacions de PideYa ja has vist el tipus de situació que els patrons resolen: codi que funciona avui però que es degrada amb cada nou requisit.

Potser et preguntes d'on va sortir aquesta idea de catalogar solucions amb nom propi. La resposta és una història curiosa que comença, ni més ni menys, en l'arquitectura d'edificis. És el tema de la lliçó següent: Història i Origen dels Patrons de Disseny.

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