Ja coneixes els cinc patrons creacionals per separat. Aquesta darrera lliçó del mòdul els posa sobre la mateixa taula, que és on es prenen les decisions reals: rarament la pregunta és "com funciona Builder?" sinó "què uso aquí: un builder, una fàbrica, o res?". Compararem intencions i costos, et donaré una guia de decisió en forma de diagrama, veurem com es combinen entre si (en el codi real gairebé mai no apareixen sols) i quina és la seva evolució típica en la vida d'un projecte. Tancarem amb el mapa creacional complet de PideYa: quin patró va quedar instal·lat a cada part del sistema i per què.
Contingut
- Els cinc, cara a cara
- Guia de decisió
- Com es combinen entre si
- L'evolució típica: els patrons es guanyen
- El mapa creacional de PideYa
- Errors comuns, exercicis i conclusió
Els cinc, cara a cara
La taula que convé tenir interioritzada (les columnes de cost reprenen la balança de la lliçó 01-06):
| Patró | Intenció en una frase | Usa'l quan... | Cost principal |
|---|---|---|---|
| Singleton | Una sola instància, amb accés global | Un recurs tècnic ha de ser únic i compartit (i no hi ha contenidor DI que el gestioni) | Estat global: dependències ocultes, tests fràgils; gairebé sempre és millor injectar |
| Factory Method | Un mètode decideix quina classe concreta instanciar; les variants el redefineixen o s'injecten | La classe del producte varia i vols afegir variants sense tocar el codi que les usa | Una parella creador/producte per variant: jerarquia o registre a mantenir |
| Abstract Factory | Un objecte fabrica una família coherent de productes | Diverses famílies intercanviables les peces de les quals no s'han de barrejar (mercats de PideYa) | Matriu de classes (variants × productes); afegir un producte trenca totes les fàbriques |
| Builder | Construir pas a pas un objecte complex, validant al final | Molts paràmetres/opcionals, validació creuada, immutabilitat desitjada | Una classe builder per producte; cerimònia excessiva per a objectes petits |
| Prototype | Crear copiant un exemplar existent | El punt de partida natural és un objecte ja configurat (repetir, plantilles) | Raonar la política de còpia (superficial/profunda) camp a camp |
Dos eixos transversals que ajuden a no confondre'ls:
- On és la dificultat? A triar la classe → fàbriques (una peça: Factory Method; família coherent: Abstract Factory). A muntar l'objecte → Builder. Al punt de partida (ja n'existeix un d'igual) → Prototype. A quantes instàncies → Singleton.
- Què se li dona al mecanisme? A una fàbrica se li donen paràmetres i decideix la classe; a un builder se li donen dades a poc a poc i munta; a un prototip no se li dona res: ja conté el seu estat.
Guia de decisió
El flux de preguntes que resumeix el mòdul. Com tota guia, orienta el 90% dels casos; el 10% restant és criteri (i recorda el filtre previ a tots: de debò fa mal el new directe?):
flowchart TD
A[Necessito crear un objecte] --> B{El 'new' directe<br/>ja fa mal?<br/>simptomes de 02-01}
B -- No --> Z[new directe o simple factory<br/>i a una altra cosa: YAGNI]
B -- Si --> C{El problema es<br/>QUANTES instancies?}
C -- "Nomes n'hi ha d'haver una" --> D[Hi ha contenidor DI?]
D -- Si --> D1[Ambit singleton<br/>del contenidor]
D -- No --> D2[Singleton<br/>holder idiom o enum]
C -- No --> E{Existeix ja un exemplar<br/>que sigui el millor planol?}
E -- Si --> F[Prototype<br/>+ registre si hi ha cataleg]
E -- No --> G{La dificultat es en<br/>el MUNTATGE?<br/>molts opcionals, validacio}
G -- Si --> H[Builder<br/>variant fluida]
G -- No --> I{Diverses peces que han de<br/>ser COHERENTS entre si?}
I -- Si --> J[Abstract Factory]
I -- No --> K[Factory Method<br/>o registre de Suppliers]
Tres consells d'ús de la guia:
- Les respostes poden ser diverses alhora: una comanda complexa (Builder) la passarel·la de cobrament de la qual tria una fàbrica de mercat (Abstract Factory) és el normal, no l'excepció. La guia s'aplica per decisió de creació, no per sistema.
- Quan dubtis entre Factory Method i Abstract Factory, la pregunta discriminant és sempre la mateixa: hi ha invariant de coherència entre diversos productes? Sense família, no hi ha Abstract Factory.
- Quan dubtis entre Builder i fàbrica: la fàbrica amaga quina classe; el builder amaga quins passos. Si el client coneix perfectament la classe (
Comanda) però pateix muntant-la, és Builder.
Com es combinen entre si
Els creacionals són peces de Lego, i en el codi real apareixen assemblats. Les combinacions canòniques, totes presents o plausibles a PideYa:
- Abstract Factory implementada amb Factory Methods. Cada mètode
crearX()deFabricaEspanyaés un factory method: la fàbrica abstracta és, estructuralment, un paquet de factory methods agrupats per variant. Ho vas veure a la lliçó 02-04. - Fàbriques exposades com a Singleton (o millor, injectades com a instància única).
FabricaMexicno té estat propi: no hi ha motiu per instanciar-la més d'una vegada. L'habitual: instància única creada a l'arrel de composició i injectada; el Singleton clàssic queda per al cas sense contenidor. - Builder dins d'una fàbrica. Una fàbrica el producte de la qual és complex el munta internament amb el seu builder:
FabricaEspanya.crearFormatadorTiquet()pot retornarFormatadorTiquet.builder().normativa(ES).moneda(EUR).build(). El client veu fàbrica; la fàbrica usa builder. Cada patró resol la seva capa. - Fàbrica que retorna el builder.
Comanda.builder(client, restaurant)—el nostre punt d'entrada de la lliçó 02-05— és un mètode estàtic de fabricació... que fabrica un builder. La combinació és tan natural que ni es nota. - Abstract Factory implementada amb Prototype. En comptes d'una classe de fàbrica per mercat, un joc d'exemplars prototípics per mercat que la fàbrica clona. Útil quan les variants es defineixen per configuració (dades) més que per codi.
- Registre (de Suppliers o de prototips) com a columna vertebral. El registre de la lliçó 02-03 i el de plantilles de la lliçó 02-06 són el mateix esquelet amb farciment diferent: receptes en un, exemplars en l'altre. Tots dos converteixen "afegir una variant" en "registrar una entrada", sense tocar el nucli.
L'evolució típica: els patrons es guanyen
La seqüència que has vist a les lliçons no és casual: és la trajectòria natural d'un punt de creació durant la vida d'un projecte sa. Convé tenir-la explícita, perquè et diu quan aturar-te:
flowchart LR
A[new directe] -->|"2a classe concreta<br/>o duplicacio del switch"| B[Simple factory<br/>idiom, no patro]
B -->|"extensio sense tocar<br/>el nucli: OCP real"| C[Factory Method<br/>o registre de Suppliers]
C -->|"apareix una 2a peca que<br/>ha de ser coherent amb la 1a"| D[Abstract Factory]
Cada fletxa té un peatge d'entrada: el símptoma que la justifica. Recórrer-la sencera "per si de cas" el primer dia és la sobreenginyeria de la lliçó 01-06; quedar-se en new directe quan ja hi ha tres switch duplicats és l'error simètric. PideYa va recórrer la seqüència completa només en pagaments i impostos (on la internacionalització l'exigia); les notificacions es van quedar al registre de Suppliers; i desenes d'objectes petits continuen feliçment en new directe. Les tres parades són correctes: cadascuna és la resposta proporcionada al seu nivell de dolor. Builder i Prototype orbiten a part d'aquesta seqüència: no evolucionen des de la fàbrica sinó des de l'objecte (constructor que s'engreixa → Builder; reconstrucció repetitiva → Prototype). I el camí invers també existeix: si les variants d'una Abstract Factory es redueixen a una i res no apunta que tornin, degradar-la a fàbrica simple és refactoritzar bé, no rendir-se (més sobre això a Refactorització Usant Patrons).
El mapa creacional de PideYa
Tanquem el mòdul com va començar: mirant el sistema sencer. Això és el que va quedar instal·lat, lliçó a lliçó, amb la seva justificació en una línia:
| Part de PideYa | Solució creacional | Per què aquesta i no una altra |
|---|---|---|
Configuració global (ConfiguracioPideYa) |
Instància única injectada des de l'arrel de composició (el Singleton clàssic va quedar com a peça didàctica) | La unicitat és decisió del desplegament; injectar-la manté dependències visibles i tests nets |
Registre d'esdeveniments (RegistreEsdeveniments) |
Singleton enum | Recurs tècnic, transversal, sense estat de negoci: el cas benigne del patró |
| Notificadors (push/SMS/email/WhatsApp) | Factory Method en la seva forma moderna: registre de Supplier<Notificador> |
Un sol producte per canal, extensió freqüent, sense invariant de família |
| Pagaments + impostos + tiquets per mercat | Abstract Factory (FabricaMercat: FabricaEspanya, FabricaMexic...) |
Tres peces amb invariant de coherència per país: la barreja ha de ser impossible per tipus |
Creació de Comanda |
Builder fluid (Comanda.builder(...)) amb validació a build() i immutabilitat |
Molts opcionals i regles creuades; la classe és coneguda, el muntatge era el problema |
| Receptes de comandes freqüents | Director modern (ReceptesComanda) sobre el builder |
Combinacions repetides mereixen nom, no cerimònia GoF completa |
| "Repetir la meva darrera comanda" | Prototype via constructor de còpia (Comanda.clonar()) |
El millor plànol de la comanda nova és la comanda vella; la còpia profunda de línies evita corrompre l'historial |
| Plantilles de carta per tipus de cuina | Prototype amb registre de prototips (RegistrePlantillesCarta) |
Catàleg d'exemplars cars de muntar, ampliable amb dades sense desplegar codi |
Objectes petits i estables (Adreca, LiniaComanda, Diners...) |
new directe o records |
Cap símptoma: qualsevol patró aquí seria soroll |
Fixa't en la darrera fila: és tan important com les altres. Un disseny madur no és el que més patrons té, sinó el que té cada patró on el seu símptoma el va justificar — la vara de mesurar que vam fixar a la lliçó de la balança.
Errors Comuns i Consells
- Triar per familiaritat, no per problema. "Uso Factory Method perquè és el que conec millor" produeix fàbriques on calia un builder. La guia de decisió existeix per forçar les preguntes correctes abans que les respostes còmodes.
- Confondre la parella estrella. Factory Method i Abstract Factory continuaran confonent-se en entrevistes i revisions; recita el discriminant: un mètode que crea un producte davant un objecte que crea una família coherent.
- Saltar-se esglaons de l'evolució. Muntar l'Abstract Factory de mercats quan PideYa només operava a Espanya hauria estat flexibilitat especulativa pura. El peatge de cada fletxa es paga quan el símptoma arriba, no abans.
- No desmuntar patrons que ja no paguen. L'evolució també va cap enrere: mantenir una jerarquia de fàbriques per a una única variant des de fa dos anys és deute estructural amb disfressa de disseny.
- Tractar les combinacions com a excepcions. Un builder dins d'una fàbrica, o una fàbrica que retorna builders, no és "barrejar patrons": és el normal. Cada patró governa una capa de la decisió de creació.
- Consell: en el teu proper projecte, fes l'exercici del mapa: una taula com la de PideYa amb cada punt de creació rellevant, la seva solució i la seva justificació en una línia. Si alguna fila no es pot justificar amb un símptoma, tens un candidat a simplificar.
Exercicis
Exercici 1: diagnòstic exprés
Per a cada situació nova a PideYa, tria el patró creacional (o l'absència de patró) i justifica-ho en una frase amb el discriminant adequat:
- El mòdul d'informes ha de generar un objecte
PanellEstadistiquesamb 12 widgets configurables, llindars opcionals i validació de rangs. - Màrqueting vol campanyes d'email el contingut de les quals es defineix visualment en un panell d'administració; cada campanya nova parteix d'una d'existent "que va funcionar".
- En integrar recollida a botiga per a supermercats, cada cadena (Mercadona, Carrefour) exigeix la seva API d'estoc, el seu format de codi de barres i la seva passarel·la de fidelització, sempre a joc.
- Un desenvolupador proposa fer singleton la
CistellaActual"per accedir-hi des de qualsevol pantalla de l'app". - Cal crear l'objecte
ExportadorDadesadequat (CSV, JSON o Parquet) segons un paràmetre; avui hi ha tres formats i no es preveuen famílies ni coherències.
Exercici 2: detectar la combinació
Descriu quins patrons creacionals col·laboren en aquest fragment i quin paper compleix cadascun:
public class FabricaMexic implements FabricaMercat {
@Override
public FormatadorTiquet crearFormatadorTiquet() {
return FormatadorTiquet.builder()
.normativa(Normativa.MX)
.moneda(Moneda.MXN)
.llegenda("Este ticket no es un CFDI")
.build();
}
// ...
}Exercici 3: l'evolució d'un punt de creació
El generador de codis promocionals de PideYa va néixer com a new GeneradorCodis() en un únic servei. Avui: (a) màrqueting demana codis alfanumèrics per a campanyes normals i codis QR per a campanyes de cartelleria; (b) el switch que els distingeix ja està copiat en dos serveis. Descriu l'evolució pas a pas que aplica la seqüència de la secció 4, indicant a quina parada t'aturaries avui i quin símptoma futur et faria avançar a la següent.
Solucions
Solució 1:
- Builder: la classe és coneguda i el problema és el muntatge (opcionals + validació creuada).
- Prototype amb registre: els exemplars es defineixen amb dades en execució i el punt de partida natural és una campanya existent; cap solució basada en classes noves no encaixa amb un panell d'administració.
- Abstract Factory: tres peces per cadena amb invariant de coherència ("sempre a joc"): família de manual.
- Cap patró, i a més rebuig raonat: la cistella és estat de negoci mutable i per usuari; un singleton la compartiria entre sessions (bug greu) i amagaria la dependència. Correspon a la sessió/context, injectada.
- Simple factory (o registre de
Suppliersi es preveuen més formats): un producte, sense famílies; amb tres casos estables, l'idiom basta i el patró complet seria cerimònia.
Solució 2: hi col·laboren Abstract Factory (la classe FabricaMexic, que garanteix la família coherent del mercat mexicà), Factory Method (el mètode crearFormatadorTiquet() és un dels seus factory methods: decideix el producte concret d'aquesta variant) i Builder (FormatadorTiquet.builder()...build() munta el producte complex pas a pas, amb la validació que correspongui a build()). Tres capes de la mateixa decisió: quina família → quin producte → com es munta.
Solució 3: evolució raonable: (1) el new directe va deixar de bastar quan va aparèixer la segona classe concreta (alfanumèric/QR) i el switch es va duplicar: símptomes clars → extreure una simple factory (FabricaGeneradorsCodi.crear(tipus)) que deduplica i centralitza. (2) Parada actual: aquí m'aturaria avui — dues variants estables i un sol punt de decisió no justifiquen més. (3) Símptoma que faria avançar a Factory Method/registre de Suppliers: noves variants amb cadència real (codis per partner, per app externa) o la necessitat que altres mòduls registrin generadors sense tocar el nucli. (4) Abstract Factory només entraria si algun dia cada campanya exigís diverses peces coherents (generador + validador + render, a joc per tipus de campanya); avui aquesta família no existeix, així que anticipar-la seria especulació.
Conclusió
El mòdul va començar amb una pregunta —qui decideix quina classe concreta s'instancia, i on?— i ara tens cinc respostes amb els seus discriminants: Singleton per a la unicitat (millor, injecció), Factory Method per variar el producte, Abstract Factory per blindar famílies, Builder per domar el muntatge, Prototype per partir d'un exemplar. Saps que es combinen per capes, que s'hi arriba per una evolució guiada per símptomes —i que s'abandonen igual—, i tens el mapa de PideYa com a plantilla del resultat final: cada patró on el seu dolor el va justificar, i new directe on no hi havia dolor.
Amb el naixement dels objectes resolt, la següent pregunta és inevitable: un cop creades les peces —passarel·les, notificadors, comandes—, com es connecten i organitzen entre si sense que el conjunt es torni un embolic? Aquesta és la segona família del catàleg: adaptar interfícies que no encaixen, compondre estructures en arbre, embolcallar objectes amb responsabilitats extra, simplificar subsistemes sencers rere una façana. Ens veiem a la Introducció als Patrons Estructurals.
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
