Els patrons de disseny es comuniquen amb diagrames: la secció "Estructura" de qualsevol catàleg és un diagrama de classes, i les col·laboracions entre objectes es mostren amb diagrames de seqüència. Si no llegeixes aquests diagrames amb fluïdesa, cada patró et costarà el doble. La bona notícia: no necessites dominar UML sencer (que té 14 tipus de diagrames); per a aquest curs en tenim prou amb dos —classes i seqüència— i un grapat de relacions. En aquesta lliçó aprendràs exactament aquest subconjunt, veuràs com el dibuixarem amb mermaid a la resta del curs, i practicaràs amb les classes de PideYa que ja coneixes. No és un curs d'UML: és el teu kit de supervivència per llegir patrons.
Contingut
- Què és UML i quina part necessitem
- El diagrama de classes: classes, atributs i mètodes
- Interfícies i classes abstractes
- Les sis relacions que has de reconèixer
- El diagrama de seqüència bàsic
- Com dibuixarem tot això al curs: mermaid
Què és UML i quina part necessitem
UML (Unified Modeling Language) és la notació estàndard (des de 1997) per dibuixar sistemes orientats a objectes. És enorme, però els catàlegs de patrons en fan servir una fracció mínima:
| Diagrama | Què mostra | Per a què el fan servir els patrons |
|---|---|---|
| De classes | Estructura estàtica: classes, interfícies i les seves relacions | La secció "Estructura" de cada patró |
| De seqüència | Interacció dinàmica: quins missatges s'envien els objectes i en quin ordre | La secció "Col·laboracions" |
Tota la resta (casos d'ús, activitats, estats, desplegament...) queda fora d'aquest curs. Regla d'or en llegir diagrames de patrons: són esquemàtics, no plànols de construcció. Mostren els participants i relacions essencials, ometent getters, constructors i detalls accessoris.
El diagrama de classes: classes, atributs i mètodes
Una classe es dibuixa com una caixa amb tres compartiments: nom, atributs i mètodes.
classDiagram
class Comanda {
-Long id
-List~LiniaComanda~ linies
-EstatComanda estat
+calcularTotal() double
+afegirLinia(LiniaComanda linia) void
+canviarEstat(EstatComanda nou) void
}
Com es llegeix cada línia:
- Visibilitat (el símbol inicial):
+públic,-privat,#protegit,~de paquet. Als diagrames de patrons gairebé sempre veuràs+en mètodes i-en atributs: l'encapsulació estàndard. - Atributs:
-Long idsignifica "atribut privatidde tipusLong". En mermaid els genèrics s'escriuen amb~:List~LiniaComanda~ésList<LiniaComanda>. - Mètodes:
+calcularTotal() doubleés un mètode públic sense paràmetres que retornadouble. El tipus de retorn va al final (en UML clàssic s'escriu: double; mermaid ho admet sense els dos punts). - Un membre subratllat és estàtic; en mermaid es marca amb
$al final:+getInstancia()$ Comanda. Un en cursiva és abstracte; en mermaid, amb*:+cuinar()*.
En Java, aquesta caixa correspon a:
public class Comanda {
private Long id;
private List<LiniaComanda> linies;
private EstatComanda estat;
public double calcularTotal() { /* ... */ return 0; }
public void afegirLinia(LiniaComanda linia) { /* ... */ }
public void canviarEstat(EstatComanda nou) { /* ... */ }
}Interfícies i classes abstractes
Els patrons viuen de les abstraccions ("programa contra una interfície", com vam veure a la lliçó anterior), així que distingir-les en un diagrama és vital:
- Una interfície es marca amb l'estereotip
<<interface>>sobre el nom. - Una classe abstracta es marca amb
<<abstract>>(o el nom en cursiva, en UML clàssic).
classDiagram
class Notificador {
<<interface>>
+notificar(Client client, String missatge) void
}
class MetodePagamentBase {
<<abstract>>
-String comerciId
+processar(double quantia)* ResultatPagament
+registrarIntent() void
}
Diferència pràctica en llegir un patró: una interfície només declara contracte; una classe abstracta pot a més aportar codi comú a les seves filles (com registrarIntent() a dalt) deixant abstracte el que varia (processar, marcat amb *). Molts patrons es recolzen just en aquesta combinació.
Les sis relacions que has de reconèixer
Aquí hi ha el 80% del valor d'aquesta lliçó. Tota la "gramàtica" dels diagrames de patrons són aquestes sis fletxes:
| Relació | Significat | Notació UML | En mermaid | Exemple PideYa |
|---|---|---|---|---|
| Herència | "és un" (estén una classe) | Fletxa amb triangle buit cap al pare | `< | --` |
| Implementació | "compleix el contracte de" una interfície | Triangle buit amb línia discontínua | `< | ..` |
| Associació | "coneix" (referència duradora) | Línia contínua (opcionalment amb fletxa) | --> |
Comanda coneix Client |
| Agregació | "té un" (tot-part feble: les parts sobreviuen al tot) | Rombe buit al costat del tot | o-- |
Repartidor agrupa Vehicle (el vehicle existeix sense ell) |
| Composició | "està compost de" (tot-part fort: les parts moren amb el tot) | Rombe ple al costat del tot | *-- |
Comanda es compon de LiniaComanda |
| Dependència | "usa temporalment" (paràmetre, variable local, creació) | Fletxa discontínua | ..> |
GeneradorFactures usa Comanda com a paràmetre |
I així es veuen totes juntes sobre classes de PideYa:
classDiagram
class Notificador {
<<interface>>
+notificar(Client c, String msg) void
}
class MetodePagamentBase {
<<abstract>>
+processar(double quantia)* ResultatPagament
}
MetodePagamentBase <|-- PagamentTargeta : herència
Notificador <|.. NotificadorSms : implementació
Comanda --> Client : associació
Repartidor o-- Vehicle : agregació
Comanda *-- LiniaComanda : composició
GeneradorFactures ..> Comanda : dependència
Claus per no confondre-les:
- El triangle sempre apunta a l'abstracció (pare o interfície). Línia contínua = herència de classe; discontínua = implementació d'interfície.
- El rombe va al costat del "tot", no de la part. Per distingir agregació de composició pregunta't: si esborro el tot, té sentit que la part continuï existint? Si la comanda es cancel·la i s'esborra, les seves línies no signifiquen res soltes → composició (rombe ple). Si el repartidor es dona de baixa, la moto continua existint a la flota → agregació (rombe buit).
- Associació davant de dependència: associació és un atribut (referència que es conserva); dependència és un ús puntual (paràmetre o variable local). En Java:
private Client client;és associació;generarPdf(Comanda c)és dependència. - Les associacions poden portar multiplicitat:
"1" --> "0..*"es llegeix "una comanda té de zero a moltes línies". En mermaid:Comanda "1" *-- "1..*" LiniaComanda.
La correspondència en Java de les tres relacions "de tenir":
public class Comanda {
private Client client; // Associacio
private final List<LiniaComanda> linies = new ArrayList<>(); // Composicio:
// les linies es creen dins de la comanda i ningu mes no les referencia
public void afegirLinia(Plat plat, int quantitat) {
linies.add(new LiniaComanda(plat, quantitat)); // neixen i moren amb ella
}
}
public class Repartidor {
private Vehicle vehicle; // Agregacio:
public void assignarVehicle(Vehicle v) { this.vehicle = v; } // ve de fora
}Matís honest: en Java la diferència agregació/composició no la imposa el llenguatge (totes dues són referències); és una intenció de disseny sobre cicle de vida i propietat. Als diagrames de patrons no sol ser crítica: si dubtes, llegeix-la com a "té un".
El diagrama de seqüència bàsic
El diagrama de classes diu qui és qui; el de seqüència diu què passa quan el sistema s'executa: quins objectes hi participen, quins missatges (crides a mètodes) s'envien i en quin ordre temporal (el temps flueix cap avall).
Escenari PideYa: un client confirma la seva cistella i el sistema cobra i notifica.
sequenceDiagram
participant C as Client
participant SC as ServeiComandes
participant MP as MetodePagament
participant N as Notificador
C->>SC: confirmar(cistella)
activate SC
SC->>SC: crearComanda(cistella)
SC->>MP: processar(quantia)
activate MP
MP-->>SC: ResultatPagament
deactivate MP
SC->>N: notificar(client, "Comanda confirmada")
SC-->>C: comandaConfirmada
deactivate SC
Elements que has de reconèixer:
- Participants (a dalt): els objectes involucrats. De cadascun en penja la seva línia de vida vertical.
- Missatge síncron (
->>, fletxa contínua): una crida a mètode; l'emissor espera la resposta. - Resposta (
-->>, fletxa discontínua): el valor retornat. - Barra d'activació (el rectangle sobre la línia de vida,
activate/deactivate): l'interval en què aquell objecte està executant alguna cosa. - Automissatge (
SC->>SC): l'objecte crida un mètode propi.
Als patrons, el diagrama de seqüència respon la pregunta que el de classes no pot respondre: "d'acord, aquestes són les classes... però qui crida qui i quan?". Veuràs que en diversos patrons l'estructura de classes és gairebé idèntica i el que els distingeix és la seqüència; per això convé llegir sempre tots dos.
Com dibuixarem tot això al curs: mermaid
En aquest curs tots els diagrames estan escrits en mermaid, una notació de text que es renderitza com a diagrama. Avantatge per a tu: pots copiar qualsevol diagrama del curs, enganxar-lo a mermaid.live i modificar-lo per experimentar. La xuleta completa del que farem servir:
classDiagram %% inicia un diagrama de classes
class LaMevaClasse {
<<interface>> %% o <<abstract>>
-tipus atributPrivat
+metode(Tipus param) TipusRetorn
+metodeAbstracte()* Tipus %% * = abstracte
+metodeEstatic()$ Tipus %% $ = estatic
}
Pare <|-- Filla %% herencia
Interficie <|.. Implementacio %% implementacio
A --> B : coneix %% associacio (amb etiqueta opcional)
Tot o-- Part %% agregacio
Tot *-- Part %% composicio
Usuari ..> Usat %% dependencia
A "1" --> "0..*" B %% multiplicitat
sequenceDiagram %% inicia un diagrama de sequencia
participant A as NomLlegible
A->>B: crida(args) %% missatge sincron
B-->>A: resposta %% retorn
activate B %% comenca activacio
deactivate B %% acaba activacioAmb aquesta xuleta pots llegir el 100% dels diagrames dels mòduls 2 a 6. Quan al mòdul 2 vegis l'estructura d'un patró, ja no estaràs desxifrant fletxes: estaràs llegint disseny.
Errors Comuns i Consells
- Confondre el sentit del triangle d'herència. El triangle toca l'abstracció (pare/interfície). En mermaid,
MetodePagamentBase <|-- PagamentTargetaes llegeix "PagamentTargeta hereta de MetodePagamentBase". Si ho llegeixes al revés, entendràs tots els patrons del revés. - Confondre el costat del rombe. El rombe va enganxat al tot (el contenidor), no a la part.
Comanda *-- LiniaComanda: el rombe és aComanda. - Intentar que el diagrama ho digui tot. Un diagrama de patró és un esquema: si no hi apareixen els getters o el constructor, no és que no existeixin; és que no importen per entendre el patró. No els afegeixis en dibuixar els teus.
- Ignorar la diferència entre línia contínua i discontínua. Contínua = relació forta i estructural (herència, associació); discontínua = relació més feble o de contracte (implementació, dependència). Aquest matís canvia el significat del diagrama.
- Llegir només el diagrama de classes i saltar-se el de seqüència. Diversos patrons comparteixen una estructura estàtica gairebé idèntica; la diferència és a la dinàmica. Acostuma't des d'ara a llegir tots dos.
- Consell: practica el camí invers. Pren una classe petita d'un projecte teu i dibuixa-la en mermaid amb les seves relacions reals. Dibuixar és el que fixa la notació; només llegir, no.
Exercicis
Exercici 1: de diagrama a Java
Tradueix aquest diagrama a esquelets de classes Java (sense implementar els cossos):
classDiagram
class Promocio {
<<interface>>
+calcularDescompte(Comanda comanda) double
}
Promocio <|.. PromocioPrimeraCompra
CalculadoraDescomptes ..> Promocio
CalculadoraDescomptes ..> Comanda
Comanda "1" *-- "1..*" LiniaComanda
Exercici 2: de Java a diagrama
Dibuixa en mermaid (classDiagram) les classes i relacions d'aquest codi, triant bé entre associació, agregació, composició i dependència:
public class Restaurant {
private final Carta carta = new Carta(); // creada i posseida pel restaurant
private List<Repartidor> repartidorsFavorits; // s'assignen des de la flota comuna
public Factura facturarMes(GeneradorFactures generador) {
return generador.generarMensual(this);
}
}Exercici 3: llegir una seqüència
Observa el diagrama de seqüència de la secció 5 i respon: (a) quin objecte orquestra el procés?, (b) en quin moment està actiu MetodePagament i què retorna?, (c) el missatge a Notificador espera resposta segons el diagrama? Quin missatge és un automissatge?
Solucions
Solució 1:
public interface Promocio {
double calcularDescompte(Comanda comanda);
}
public class PromocioPrimeraCompra implements Promocio { // <|.. implementacio
public double calcularDescompte(Comanda comanda) { return 0; }
}
public class CalculadoraDescomptes {
// ..> dependencia: usa Promocio i Comanda com a parametres, no com a atributs
public double calcular(Comanda comanda, Promocio promocio) { return 0; }
}
public class Comanda {
// *-- composicio 1 a 1..*: la comanda posseeix les seves linies (almenys una)
private final List<LiniaComanda> linies = new ArrayList<>();
}
public class LiniaComanda { }Solució 2:
classDiagram
Restaurant "1" *-- "1" Carta : composició
Restaurant o-- Repartidor : agregació
Restaurant ..> GeneradorFactures : dependència
Restaurant ..> Factura : dependència
Raonament: la Carta la crea i posseeix el restaurant (mor amb ell) → composició. Els repartidors existeixen fora del restaurant i només s'hi associen com a favorits → agregació (una associació simple --> també seria defensable; l'important és descartar la composició). GeneradorFactures i Factura només apareixen com a paràmetre i valor de retorn d'un mètode → dependències.
Solució 3: (a) ServeiComandes: rep la petició del client i crida tots els altres. (b) MetodePagament està actiu només entre la crida processar(quantia) i la seva resposta; retorna un ResultatPagament. (c) Tal com està dibuixat, a notificar(...) no se li dibuixa fletxa de retorn: el diagrama no mostra resposta (es llegeix com una crida el resultat de la qual no interessa en aquest escenari). L'automissatge és SC->>SC: crearComanda(cistella): ServeiComandes invocant un mètode propi.
Conclusió
Ja tens el kit de lectura de diagrames que faràs servir en tot el curs: la caixa de classe amb visibilitats, els estereotips <<interface>> i <<abstract>>, les sis relacions (herència, implementació, associació, agregació, composició i dependència, amb les seves fletxes i rombes ben orientats) i el diagrama de seqüència per a la dinàmica. A més coneixes la sintaxi mermaid exacta amb què estan escrits tots els diagrames del curs, llesta per copiar i experimentar.
Amb les eines de lectura a la mà, toca desplegar el mapa: quants patrons hi ha, com s'organitzen i què fa cada família? Aquest mapa —que és també l'itinerari dels mòduls 2, 3 i 4— és la propera lliçó: Classificació dels Patrons de Disseny.
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
