Benvingut al primer pis de l'edifici. Al mòdul 1 vam deixar els fonaments posats: saps què és un patró, quins principis encarna i com sospesar-ne el cost. Ara toca la primera família del catàleg GoF, la que governa el naixement dels objectes. Pot semblar un tema menor —quin problema hi pot haver a fer new?— però veuràs que la instrucció més innocent de Java és, en realitat, el punt d'acoblament més rígid de tot un sistema. Aquesta lliçó presenta el problema comú que els cinc patrons creacionals resolen, et dona el mapa de la família i t'ensenya a reconèixer en el codi de PideYa els símptomes que en cal un. Encara no desenvoluparem cap patró: això comença a la propera lliçó.
Contingut
- El problema:
newés un contracte de per vida - Per què crear objectes és una responsabilitat en si mateixa
- Panorama dels cinc patrons creacionals
- Símptomes a PideYa: com es nota que falta un creacional
- Àmbit de classe vs. àmbit d'objecte en els creacionals
- Conclusió
El problema: new és un contracte de per vida
Comencem per un fragment real del checkout de PideYa, tal com podria haver-se escrit la primera setmana del projecte:
public class ServeiCheckout {
public void confirmarComanda(Cistella cistella) {
// Cobrar
PassarellaStripe passarella = new PassarellaStripe("sk_live_pideya_2026");
passarella.cobrar(cistella.getTotal(), cistella.getClient().getTargeta());
// Avisar el client
NotificadorPush notificador = new NotificadorPush();
notificador.enviar(cistella.getClient(), "Comanda confirmada!");
}
}Funciona. I tanmateix, cadascun d'aquests new és una decisió cablejada al codi que lliga ServeiCheckout de tres maneres:
- Lliga a una classe concreta.
ServeiCheckoutno depèn d'"una passarel·la de pagament" sinó de Stripe en particular. Si demà negoci signa amb Redsys, cal obrir aquesta classe i editar-la. És exactament la violació de DIP que vam veure a la lliçó de principis: un mòdul d'alt nivell depenent d'un detall. - Lliga al procés de construcció.
ServeiCheckoutsap que la passarel·la necessita una clau secreta, i la té incrustada. Qualsevol canvi en com es construeix la passarel·la (més paràmetres, configuració per entorn) propaga canvis fins aquí. - Tanca la porta a l'extensió. Afegir un nou canal de notificació o una nova passarel·la obliga a modificar codi que ja funcionava: violació d'OCP. I als tests, no hi ha manera de substituir la passarel·la real per un doble: cada test de checkout intentaria cobrar de debò.
La primera màxima del GoF —«programa contra interfícies, no contra implementacions»— té una lletra petita que ara podem llegir: encara que tot el teu codi faci servir interfícies (PassarellaPagament, Notificador), en algun lloc algú ha d'escriure el new de la classe concreta. Les interfícies no s'instancien. Els patrons creacionals responen precisament la pregunta que aquesta lletra petita deixa oberta:
On, com i qui decideix quina classe concreta s'instancia, perquè la resta del sistema no ho hagi de saber?
Per què crear objectes és una responsabilitat en si mateixa
La idea central de tota la família es pot resumir així: crear un objecte és una responsabilitat, i com tota responsabilitat (SRP), mereix de vegades la seva pròpia classe o el seu propi mètode. Els patrons creacionals encapsulen dos tipus de coneixement perquè no es dispersin pel sistema:
- Què es crea: quina classe concreta s'instancia (
PassarellaStripeoPassarellaRedsys?NotificadorPushoNotificadorSms?). - Com es crea: quins passos, paràmetres, validacions o objectes col·laboradors calen per deixar la instància a punt per usar.
Quan aquest coneixement està encapsulat, el codi client treballa només contra interfícies i el sistema guanya la propietat que perseguim des del mòdul 1: els eixos de canvi reals (nova passarel·la, nou país, nou canal) s'absorbeixen afegint peces, no modificant les existents.
Un matís important que convé fixar des d'ara: els creacionals no eliminen el new, el reubiquen. El new PassarellaStripe(...) continuarà existint, però viurà en un únic lloc dissenyat per a això (una fàbrica, un builder, un mètode de creació), en comptes d'estar repartit per vint classes de negoci.
Panorama dels cinc patrons creacionals
El GoF cataloga cinc patrons creacionals. Aquí tens el mapa de la família en una línia per patró; cadascun tindrà la seva lliçó completa en aquest mòdul:
| Patró | Idea en una línia | Lliçó |
|---|---|---|
| Singleton | Garantir que una classe té una única instància i donar-hi un punt d'accés global | 02-02 |
| Factory Method | Delegar en subclasses la decisió de quina classe concreta instanciar, rere un mètode de creació | 02-03 |
| Abstract Factory | Crear famílies d'objectes relacionats (que s'han de combinar entre si) sense anomenar les seves classes concretes | 02-04 |
| Builder | Construir pas a pas objectes complexos, separant la construcció de la representació final | 02-05 |
| Prototype | Crear objectes nous clonant una instància existent en comptes d'instanciar una classe | 02-06 |
Fixa't que cada patró ataca una aresta diferent del mateix problema:
- Singleton controla quantes instàncies existeixen.
- Factory Method i Abstract Factory controlen quina classe concreta es tria (l'un per a un producte, l'altre per a famílies senceres).
- Builder controla el procés de construcció quan és llarg o té variants.
- Prototype controla la font: el nou objecte no neix d'una classe sinó d'un altre objecte.
A la comparativa final del mòdul els enfrontarem entre si i veuràs com es combinen; de moment n'hi ha prou amb aquest mapa.
Símptomes a PideYa: com es nota que falta un creacional
Com vam aprendre a la lliçó de la balança, els patrons es guanyen, no es planten: s'hi arriba quan el codi fa mal. Aquests són els dolors concrets —tots reals en el creixement d'una plataforma com PideYa— que assenyalen cap a aquesta família. Guarda'ls com a llista de diagnòstic; les lliçons següents els aniran resolent un a un:
newde classes concretes repartits pel codi de negoci. Cercarnew PassarellaStriperetorna dotze resultats en set classes. Canviar de proveïdor és una operació quirúrgica a cor obert. → El coneixement de què crear demana ser centralitzat (Factory Method).- Un
switcho cadena d'ifsobre un "tipus" que decideix què instanciar, duplicat allà on es necessita l'objecte:
// Aquest bloc apareix, amb variacions, en tres serveis diferents de PideYa
Notificador n;
switch (canalPreferit) {
case PUSH: n = new NotificadorPush(); break;
case SMS: n = new NotificadorSms(clauTwilio); break;
case EMAIL: n = new NotificadorEmail(servidorSmtp); break;
default: throw new IllegalArgumentException(canalPreferit.toString());
}Cada canal nou obliga a trobar i editar totes les còpies. → Centralitzar la decisió (Factory Method).
- Objectes que han de ser coherents entre si i ningú no ho garanteix. En obrir PideYa a Mèxic, un descuit va combinar la passarel·la mexicana amb la calculadora d'IVA espanyol: cobraments mal calculats en producció. Les peces es creaven soltes i res no impedia barrejar famílies. → Crear la família completa d'una vegada (Abstract Factory).
- Constructors telescòpics. El constructor de
Comandaha anat engreixant-se amb cada sprint fins a tornar-se il·legible i impossible de cridar sense consultar la signatura:
Comanda c = new Comanda(client, linies, adreca, null, null, "sense ceba",
true, null, FranjaHoraria.MIGDIA, false);
// Que es cada null? Que significa el true? Ningu no ho sap sense mirar la signatura.→ Construcció pas a pas, llegible i validada (Builder).
- Crear un objecte "gairebé igual" a un altre d'existent costa reconstruir-lo sencer. La funció "repetir la meva darrera comanda" de l'app copia camp a camp la comanda anterior, i cada cop que
Comandaguanya un atribut algú s'oblida de copiar-lo. → Clonar en comptes de reconstruir (Prototype). - Estat global casolà. La configuració de la plataforma (comissions, radis de repartiment) es carrega tres vegades des de disc perquè cada servei fa la seva pròpia còpia, i en producció dues còpies van quedar dessincronitzades. → Controlar la unicitat (Singleton... amb les alertes que veurem).
- Tests que no poden aïllar el subjecte. Provar
ServeiCheckoutexigeix xarxa, claus reals i paciència, perquè els seus col·laboradors s'instancien a dins ambnewi no hi ha manera de colar-hi un doble de prova.
Si reconeixes diversos d'aquests símptomes en un codi, tens un problema anomenable sense citar cap patró (primer filtre de la lliçó anterior): la creació d'objectes s'ha convertit en un eix de canvi real i adolorit. Aquest és el moment —i no abans— d'obrir el catàleg creacional.
Àmbit de classe vs. àmbit d'objecte en els creacionals
A la classificació del catàleg vam veure que el GoF creua el propòsit (creacional, estructural, de comportament) amb l'àmbit: si el patró fixa la seva solució mitjançant herència (àmbit de classe, decidit en compilació) o mitjançant composició i delegació (àmbit d'objecte, configurable en execució). En els creacionals el repartiment és molt asimètric:
| Àmbit | Patrons | Mecanisme de variació |
|---|---|---|
| Classe | Factory Method | Es varia heretant: cada subclasse del creador redefineix quin producte instancia. L'elecció queda fixada per la subclasse que usis. |
| Objecte | Abstract Factory, Builder, Prototype, Singleton | Es varia component: el client rep (o localitza) un objecte —una fàbrica, un builder, un prototip— i es pot canviar per un altre en temps d'execució. |
Aquesta asimetria no és casual: reflecteix la segona màxima del GoF, «afavoreix la composició per sobre de l'herència». Quatre dels cinc creacionals deleguen la creació en un objecte que es pot injectar, substituir o configurar sense recompilar; només Factory Method es recolza en l'herència, i tot i així veurem a la seva lliçó variants modernes (lambdes, genèrics) que el desplacen també cap a la composició. Quan estudiïs cada patró, pregunta't sempre en quin àmbit és: et dirà quan es decideix la classe concreta (en compilar o en executar) i, per tant, quanta flexibilitat compres i a quin preu.
Errors Comuns i Consells
- Creure que l'objectiu és "prohibir el
new". No: l'objectiu és que elnewde cada classe concreta visqui en un únic lloc amb nom propi. Unnew ArrayList<>()local, o elnewd'un objecte de valor simple comDiners, no necessiten cap patró. - Començar el disseny triant el patró. L'ordre correcte és símptoma → problema → patró. Si el checkout de PideYa tingués una sola passarel·la estable des de fa anys, els
newdirectes serien perfectament defensables (YAGNI). - Confondre els patrons entre si abans d'estudiar-los. És normal que ara mateix Factory Method i Abstract Factory et sonin iguals; no intentis distingir-los encara de memòria. Les lliçons següents els separen amb exemples, i la comparativa els posarà cara a cara.
- Oblidar els tests com a símptoma. "No puc testejar aquesta classe sense aixecar mig sistema" és un dels indicadors més fiables que la creació està mal ubicada. Escolta els teus tests: són el primer client del teu disseny.
Exercicis
Exercici 1: caçar els acoblaments
En aquest fragment del servei d'alta de restaurants de PideYa, identifica tots els punts on el codi queda acoblat a una classe concreta o a un procés de construcció, i classifica cadascun segons el símptoma de la secció 4 que millor el descrigui:
public class ServeiAltaRestaurant {
public void donarDAlta(DadesRestaurant dades) {
ValidadorCif validador = new ValidadorCif();
if (!validador.validar(dades.getCif())) throw new CifInvalidException();
Restaurant r = new Restaurant(dades.getNom(), dades.getCif(),
dades.getAdreca(), null, null, true, false, null, 30, null);
NotificadorEmail email = new NotificadorEmail("smtp.pideya.com", 587, "[email protected]");
email.enviar(dades.getEmailContacte(), "Benvingut a PideYa");
}
}Exercici 2: assignar patró a cada símptoma
Sense implementar res (encara no saps com), indica quin patró creacional del panorama de la secció 3 sembla apuntar a cada situació de PideYa, i justifica-ho en una frase:
- L'app permet "duplicar" una carta de restaurant sencera per crear la carta del torn de nit amb petits canvis.
- Els repartidors autònoms i els de flota pròpia requereixen objectes diferents de contracte, assegurança i liquidació, que han de ser sempre del mateix règim entre si.
- L'objecte
Facturaté 4 camps obligatoris i 9 d'opcionals, i el seu constructor és immanejable. - La connexió al broker de missatgeria ha de ser una de sola per a tot el procés i accessible des de diversos serveis.
- El servei d'avisos ha de crear el notificador adequat (push, SMS, email) segons la preferència del client, i avui ho fa amb un
switchrepetit en tres classes.
Exercici 3: patró o YAGNI?
PideYa genera un identificador de comanda amb new GeneradorIdComanda().seguent(). El generador no té estat de configuració, existeix una sola implementació i no hi ha plans de canviar-la. Un company proposa "preparar-lo per al futur" amb una fàbrica. Aplica els filtres de la lliçó 01-06 i dona el teu veredicte.
Solucions
Solució 1:
new ValidadorCif(): dependència de classe concreta dins del negoci; impedeix substituir-la per un doble als tests (símptoma "tests que no poden aïllar el subjecte" i "newrepartits").new Restaurant(...)amb deu arguments, diversosnulli literals sense nom (true, false, 30): constructor telescòpic de manual (símptoma → Builder).new NotificadorEmail("smtp...", 587, ...): doble acoblament, a la classe concreta (i si el client prefereix SMS?) i al procés de construcció (host i port cablejats aquí, que és el pitjor lloc per a ells). Símptomes "newrepartits" i coneixement de construcció dispers (→ Factory Method).
Solució 2:
- Prototype: l'objecte nou neix clonant una carta existent, no reconstruint-la de zero.
- Abstract Factory: contracte + assegurança + liquidació formen una família que s'ha de mantenir coherent (tot del règim autònom o tot de flota).
- Builder: construcció pas a pas amb obligatoris i opcionals, validant al final.
- Singleton (amb les reserves que veurem a la propera lliçó): unicitat d'instància i accés compartit.
- Factory Method: centralitzar en un mètode de creació la decisió de quin notificador concret instanciar.
Solució 3: falla el filtre 2 (l'eix de canvi és especulatiu: una sola implementació, sense plans) i el 3 (el codi simple no fa mal: el generador no té dependències que dificultin tests). Veredicte: no introduir fàbrica; com a màxim, si GeneradorIdComanda s'usa en classes que volen testejar-se de forma determinista, injectar-lo com a col·laborador (DIP bàsic) sense patró addicional. La fàbrica es guanyarà el dia que existeixi una segona manera real de generar identificadors.
Conclusió
Ja tens el mapa de la família creacional: l'enemic comú és l'acoblament que cada new cableja entre el codi de negoci i les classes concretes (amb OCP i DIP com a principis ferits), i els cinc patrons són cinc maneres d'encapsular la creació —controlant quantes instàncies hi ha, quina classe es tria, com es construeix o des de quin objecte es clona—. També saps llegir els símptomes que justifiquen obrir-los i en quin àmbit (classe o objecte) juga cadascun.
Comencem la desfilada pel patró més famós, el més curt d'escriure... i el més discutit de tot el catàleg: garantir que una cosa existeixi una sola vegada és fàcil; fer-ho sense crear un problema més gros, no tant. Ens veiem a Singleton.
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
