El mòdul anterior va acabar amb el naixement dels objectes resolt: fàbriques, builders i prototips decideixen què s'instancia, com es munta i d'on es parteix. Però un sistema no és una col·lecció d'objectes ben nascuts: és una xarxa d'objectes connectats. I a PideYa aquesta xarxa ja comença a tensar-se: un SDK de pagaments extern amb una interfície que no encaixa amb la nostra PassarellaPagament, una carta de restaurant que és un arbre de seccions dins de seccions, extres de plats que multipliquen subclasses, un checkout que obliga cada pantalla a conèixer cinc subsistemes... Cap d'aquests problemes no va de crear objectes: van de compondre'ls. Aquesta és la segona família del catàleg GoF, i aquesta lliçó n'és el mapa.

Contingut

  1. El problema comú: compondre sense acoblar
  2. Panorama dels set patrons estructurals
  3. Àmbit de classe i àmbit d'objecte
  4. Símptomes a PideYa que cal un estructural
  5. Com llegirem cada patró en aquest mòdul
  6. Exercicis i conclusió

El problema comú: compondre sense acoblar

Els patrons creacionals responien una pregunta: qui decideix quina classe concreta s'instancia? Els estructurals en responen una altra: com s'assemblen classes i objectes en estructures més grans sense que l'estructura es torni rígida?

La paraula clau és estructura: relacions entre peces. I l'enemic és el de sempre, amb una altra cara: l'acoblament. Al mòdul 2 l'acoblament s'esmunyia pel new; aquí s'esmuny per les connexions:

  • Una classe que depèn de la interfície exacta d'una altra que no controlem (una API de tercers): quan el tercer canvia, o quan volem canviar de tercer, tot es trenca.
  • Una jerarquia d'herència que creix multiplicativament: cada dimensió de variació nova duplica les subclasses.
  • Un client que per fer una operació ha de conèixer i coordinar mitja dotzena d'objectes: el coneixement de l'estructura interna s'escampa per tot el sistema.
  • Estructures recursives (arbres part-tot) tractades amb if (esGrup) ... else ... pertot arreu.
  • Objectes cars (memòria, xarxa, càrrega) creats i connectats sense control, perquè ningú no fa de mitjancer en l'accés.

Els set patrons estructurals són set maneres d'organitzar les connexions perquè continuïn complint el que vam aprendre a la lliçó de principis: programar contra interfícies, preferir composició sobre herència, i poder estendre sense modificar. De fet, veuràs que gairebé tots els estructurals són aplicacions sistemàtiques de "composició sobre herència": un objecte que embolcalla, conté o remet a d'altres, en lloc d'una jerarquia que ho hereta tot.

Panorama dels set patrons estructurals

La foto completa del mòdul, cada patró en una línia. No intentis memoritzar-la ara: hi tornarem, ampliada, a la comparativa final.

Patró Intenció en una línia
Adapter Convertir la interfície d'una classe existent en la interfície que el client espera
Bridge Separar una abstracció de la seva implementació perquè totes dues evolucionin per separat
Composite Compondre objectes en arbres part-tot i tractar fulles i grups de manera uniforme
Decorator Afegir responsabilitats a un objecte dinàmicament, embolcallant-lo, sense herència
Facade Oferir una interfície simple i unificada davant d'un subsistema complex
Flyweight Compartir l'estat comú de moltíssims objectes petits per estalviar memòria
Proxy Posar un substitut amb la mateixa interfície que controla l'accés a l'objecte real

Una observació que convé fer ja, perquè evita la confusió més gran del mòdul: quatre dels set (Adapter, Decorator, Facade i Proxy) comparteixen mecànica —un objecte davant d'un altre, delegant— i es distingeixen només per la intenció. Adapter canvia la interfície; Decorator la conserva i hi afegeix responsabilitats; Facade la simplifica; Proxy la conserva i en controla l'accés. Recorda la definició de patró de la primera lliçó: context, problema, solució i intenció. En aquesta família, la intenció és sovint l'única cosa que separa un patró d'un altre; els dedicarem un acarament complet a la comparativa.

Els altres tres organitzen multituds: Composite organitza objectes en arbres, Bridge organitza jerarquies en dos eixos independents, i Flyweight organitza milers d'instàncies compartint allò comú.

Àmbit de classe i àmbit d'objecte

A la classificació GoF vam veure que cada patró té un àmbit: de classe si la relació es fixa amb herència en temps de compilació, o d'objecte si s'estableix amb composició en temps d'execució.

A la família estructural el recompte és rotund: sis dels set són d'àmbit d'objecte. Només Adapter existeix en les dues variants, i per això és l'exemple perfecte per entendre la diferència:

  • Adapter de classe: l'adaptador hereta de la classe que adapta i alhora implementa la interfície esperada. La relació queda soldada en compilar: aquell adaptador serveix per a aquella classe concreta i les seves subclasses, i prou.
  • Adapter d'objecte: l'adaptador conté una referència a l'objecte adaptat i hi delega. La relació es decideix en construir: el mateix adaptador pot embolcallar qualsevol objecte compatible, fins i tot decidit en execució.
classDiagram
    class InterficieEsperada { <<interface>> +operacio() }
    class ClasseExistent { +operacioVella() }

    class AdaptadorDeClasse { +operacio() }
    class AdaptadorDObjecte { -adaptat: ClasseExistent +operacio() }

    InterficieEsperada <|.. AdaptadorDeClasse
    ClasseExistent <|-- AdaptadorDeClasse : hereta (àmbit de classe)
    InterficieEsperada <|.. AdaptadorDObjecte
    AdaptadorDObjecte o-- ClasseExistent : conté (àmbit d'objecte)

Que gairebé tota la família sigui d'àmbit d'objecte no és casualitat: és el principi de composició sobre herència fet catàleg. L'herència fixa l'estructura per sempre; la composició permet reconfigurar-la —embolcallar, desembolcallar, recombinar— mentre el programa corre. Veuràs aquesta idea repetida a cada lliçó: el decorador s'apila en execució, l'arbre del composite es munta en execució, el pont es creua en execució.

Símptomes a PideYa que cal un estructural

Igual que el mòdul 2 va començar catalogant els dolors del new, aquest comença catalogant els dolors de la composició. Tots són reals a PideYa avui; cadascun té la seva lliçó:

Símptoma a PideYa Olor de disseny Patró que el tracta
Volem cobrar amb un SDK de PayPal heretat, però la seva interfície (makePayment(long, String)) no s'assembla gens a la nostra PassarellaPagament del mòdul 2 Interfícies incompatibles entre el nostre codi i codi que no podem tocar Adapter
Notificacions de confirmació, retard i promoció × canals push, SMS i email: 3×3 = 9 subclasses, i cada tipus o canal nou multiplica Jerarquia que creix per producte de dues dimensions independents Bridge
La carta d'un restaurant té seccions, subseccions i plats; el codi és ple de if (esSeccio) ... else ... Estructura arbre part-tot tractada amb condicionals en lloc de recursió uniforme Composite
"Doble formatge", "sense gluten", "ració gran": cada combinació d'extres sobre un plat demana la seva subclasse (PizzaAmbFormatgeSenseGluten...) Explosió combinatòria de subclasses per afegir responsabilitats Decorator
Per confirmar una comanda, l'app mòbil crida validació, impostos, passarel·la, persistència i notificacions, en l'ordre correcte, amb la gestió d'errors correcta Clients obligats a conèixer i orquestrar un subsistema sencer Facade
El mapa en temps real mostra milers de marcadors de repartidors i restaurants, i cadascun carrega la seva pròpia icona: l'app consumeix memòria com si no hi hagués demà Milers d'objectes que dupliquen el mateix estat immutable Flyweight
Les fotos dels plats pesen diversos MB i es carreguen totes en obrir la carta; i qualsevol empleat pot invocar operacions que haurien de ser només d'administradors Accés a l'objecte real sense control: sense càrrega mandrosa, sense permisos Proxy

Fixa't en el criteri, que és el mateix de tot el curs: primer el símptoma, després el patró. Cap de les lliçons següents no comença per "apliquem X"; totes comencen per un dolor concret de PideYa i arriben al patró com a resposta proporcionada. Si al teu projecte no reconeixes el símptoma, la lliçó de la balança ja et va dir què fer: res.

Com llegirem cada patró en aquest mòdul

Les set lliçons de patró segueixen el mateix esquelet que les del mòdul 2, perquè puguis comparar-los entre si sense esforç:

  1. El problema a PideYa: el símptoma de la taula anterior, amb codi del primer intent (el dolent).
  2. Estructura del patró: intenció GoF i classDiagram mermaid amb els rols del patró, tal com vam aprendre a la lliçó d'UML.
  3. Implementació Java completa, explicada pas a pas.
  4. Variants rellevants (de classe/objecte, transparent/segura, estàtica/dinàmica segons el patró).
  5. Quan usar-lo i quan no, amb l'honestedat de costos habitual.
  6. Relació amb altres patrons: només mencions amb enllaç; cada cosa es desenvolupa a la seva lliçó.
  7. Errors comuns, exercicis amb solució i conclusió que enllaça amb la lliçó següent.

I continuem construint sobre el que ja tenim: la PassarellaPagament i la família per mercat del mòdul 2 reapareixeran a Adapter i Facade; els Notificador del Factory Method seran decorats i pontats; el Comanda.Builder participarà en el checkout de la façana. Els patrons no viuen en lliçons estanques: viuen en el mateix sistema.

Exercicis

Exercici 1: classificar símptomes

Per a cada situació nova de PideYa, digues quin patró estructural de la taula sembla que hi apunta (no cal que sàpigues encara com funciona; n'hi ha prou de casar símptoma i intenció d'una línia):

  1. L'equip de dades vol consultar comandes amb una llibreria d'informes la interfície de lectura de la qual no coincideix amb el nostre repositori de comandes.
  2. Un "menú del dia" conté primer, segon i postres; i un "menú familiar" conté dos menús del dia i una beguda gran. Volem calcular el preu de qualsevol cosa que es pugui demanar, sigui plat solt o menú niat.
  3. Volem que certes crides al servei de repartiment quedin registrades a RegistreEsdeveniments i es reintentin si fallen, sense tocar la classe del servei.
  4. La pantalla "estat de la meva comanda" necessita dades de cuina, de repartiment i de pagaments; avui l'app crida els tres serveis i combina els resultats a mà.

Exercici 2: classe o objecte?

Explica amb les teves paraules per què un Adapter d'objecte pot adaptar en execució "qualsevol passarel·la vella que ens arribi" i un Adapter de classe no. Quina relació UML de la lliçó 01-04 usa cadascun?

Solucions

Solució 1:

  1. Adapter: dues interfícies que no encaixen i una d'elles (la llibreria) no és nostra.
  2. Composite: estructura part-tot recursiva (menús que contenen menús) amb una operació uniforme (preu).
  3. Decorator (amb un matís de Proxy que afinarem a les seves lliçons): afegir responsabilitats —logging, reintents— embolcallant, sense modificar la classe.
  4. Facade: un punt únic i simple que orquestra diversos subsistemes per a un cas d'ús.

Solució 2: l'Adapter d'objecte conté (composició/agregació, la relació o--) una referència tipada a allò adaptat, que se li passa en construir: en execució pot rebre una instància o una altra, fins i tot de subclasses diferents. L'Adapter de classe hereta (generalització, <|--) d'una classe concreta: la relació queda fixada en compilar i només val per a aquella classe. És exactament la diferència entre àmbit d'objecte i àmbit de classe, i la raó que "composició sobre herència" sigui el lema d'aquesta família.

Conclusió

Ja tens el mapa del mòdul: set patrons que resolen un mateix problema de fons —compondre classes i objectes en estructures més grans mantenint-les flexibles— atacant-lo per set fronts: interfícies que no encaixen, jerarquies que es multipliquen, arbres part-tot, responsabilitats afegides, subsistemes complexos, multituds d'objectes i accessos per controlar. Saps que gairebé tots són d'àmbit d'objecte (composició sobre herència portat a catàleg), i que quatre d'ells comparteixen mecànica i es distingeixen per la intenció, així que la intenció serà la nostra brúixola.

Comencem pel més immediat de tots, el que apareix sempre que el món exterior truca a la nostra porta amb una interfície que no és la nostra: PideYa necessita cobrar amb un SDK heretat que no parla PassarellaPagament, i no podem tocar ni l'SDK ni tot el codi que ja usa PassarellaPagament. La peça no encaixa... llevat que li construïm un endoll. Ens veiem a Adapter.

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