Tot el curs hem construït patrons sobre PideYa; aquesta lliçó inverteix el telescopi: els patrons que fa anys que uses sense saber-ho. Cada cop que recorres una llista amb for-each, embolcalles un stream en un BufferedReader o deixes que Spring t'injecti un bean, estàs consumint Iterator, Decorator i Singleton escrits per altres. Llegir aquests exemples té doble valor: confirmen que el catàleg no és teoria acadèmica sinó l'enginyeria de les llibreries més usades del món, i entrenen l'habilitat més rendible d'aquesta lliçó — reconèixer un patró en llegir codi aliè a partir de les seves pistes, començant pels noms. No parlarem aquí d'arquitectura (capes, microserveis, missatgeria): això és territori del mòdul 6; aquí mirem classes i APIs concretes.
Contingut
- Patrons al JDK
- Patrons a Spring
- Altres llibreries i frameworks
- Com reconèixer patrons en llegir codi aliè
- El que les convencions de noms ensenyen
- Exercicis i conclusió
Patrons al JDK
La biblioteca estàndard de Java és el catàleg de patrons en producció més gran del planeta:
| Patró | On viu al JDK | La pista |
|---|---|---|
| Iterator | java.util.Iterator, Iterable, tot el for-each |
El patró va ascendir a sintaxi del llenguatge |
| Decorator | java.io: BufferedReader, GZIPOutputStream... |
Constructors que reben un altre stream del mateix tipus |
| Observer | Listeners de Swing (ActionListener), java.util.concurrent.Flow |
addXxxListener, subscribe |
| Factory Method | Integer.valueOf, List.of, Optional.of, Files.newBufferedReader |
Estàtics que retornen el tipus... o un subtipus que tu no tries |
| Builder | StringBuilder, HttpRequest.newBuilder(), Stream.Builder |
Mètodes encadenables + build() final |
| Proxy | java.lang.reflect.Proxy (la base de mig ecosistema) |
El JDK porta el patró com a utilitat de sèrie |
| Strategy | Comparator a sort, ThreadFactory, UncaughtExceptionHandler |
L'algorisme viatja com a argument |
| Template Method | AbstractList, InputStream.read(byte[]) sobre read(), ClassLoader.loadClass |
Classe Abstract* amb mètodes per "omplir" |
| Composite | Swing: Container és un Component que conté Components |
El contenidor implementa la interfície del contingut |
| Flyweight | La memòria cau d'Integer.valueOf (−128..127), l'string pool |
Instàncies compartides per a valors repetits |
| Adapter | Arrays.asList, Collections.list(Enumeration), InputStreamReader |
Converteix una interfície (array, Enumeration, bytes) en una altra (List, Iterator, chars) |
| Prototype | Object.clone() / Cloneable |
El mecanisme existeix... amb les pegues que vam veure a 02-06 |
El cas java.io mereix zoom, perquè és el mateix apilament que els extres de plat de PideYa:
// Tres decoradors apilats sobre un nucli, componibles en qualsevol ordre raonable:
var lector = new BufferedReader( // afegeix buffering i readLine()
new InputStreamReader( // adapta bytes → caracters (un Adapter a la pila!)
new FileInputStream("comandes.csv"))); // el component concret
// I l'acarament en viu: Integer.valueOf es Factory Method AMB Flyweight a dins
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
// a == b es true: valueOf va retornar la MATEIXA instancia en cache (-128..127).
// Amb new Integer(100) (deprecat) serien objectes diferents: la fabrica
// existeix precisament per poder decidir coses aixi sense que el client ho sapiga.Aquest darrer comentari és la moralitat creacional sencera del mòdul 2: qui controla la creació controla el compartir, el desar en memòria cau i el subtipus — per això el JDK modern (List.of, Optional.of) ja gairebé no et deixa fer new.
Patrons a Spring
Spring no "usa" patrons: és patrons assemblats, i uns quants resolen crítiques que vam fer durant el curs:
- Singleton per contenidor — l'scope per defecte d'un bean: una instància... per
ApplicationContext, no per JVM. És exactament l'alternativa que vam defensar a 02-02: unicitat gestionada per qui injecta, sensegetInstance()estàtic, sense estat global intocable en tests. LaConfiguracioPideYade PideYa va acabar així. - Factory pertot arreu —
BeanFactory/ApplicationContextsón fàbriques gegants; la interfícieFactoryBean<T>et deixa escriure la teva;@Beanen una classe@Configurationés un Factory Method que el contenidor invoca per tu. - Proxy dinàmic com a columna vertebral —
@Transactional,@Cacheable,@Asynci la seguretat de mètode funcionen perquè Spring embolcalla el teu bean en un proxy (JDK dinàmic si hi ha interfície, CGLIB si no) que intercepta la crida, fa la seva màgia i delega. És el Proxy del mòdul 3 industrialitzat — i explica el clàssic "el meu@Transactionalno funciona si em crido a mi mateix": l'autocrida no passa pel proxy. - Template Method sense herència —
JdbcTemplate,RestTemplate,TransactionTemplate: l'esquelet (obrir connexió, executar, mapar, tancar, traduir excepcions) és fix; el teu pas variable entra com a lambda/callback. La variant per composició de l'exercici final de 04-11:
// El template fixa el flux (connexio, statement, bucle, tancament, excepcions);
// tu nomes aportes el pas variable: com mapar una fila.
List<Comanda> comandes = jdbcTemplate.query(
"SELECT * FROM comandes WHERE estat = ?",
(fila, num) -> new Comanda(fila.getLong("id"), fila.getString("estat")), // el teu "hook"
"EN_REPARTIMENT");- Observer als esdeveniments —
ApplicationEventPublisher.publishEvent(...)+@EventListener: difusió a interessats anònims, idèntica en intenció a l'ObservadorComandade PideYa (amb bonus:@TransactionalEventListenerper escoltar "quan el commit sigui real"). - Strategy institucionalitzat — injectar una interfície amb N implementacions (
List<ReglaDescompte>autowired, o triar per@Qualifier) és Strategy amb el contenidor com a selector. - Adapter i Facade interns — els
HandlerAdapterde Spring MVC adapten tipus de controlador heterogenis;DispatcherServletés la façana de tot el processament web.
Altres llibreries i frameworks
- Hibernate / JPA:
Session/EntityManagercom a Facade del motor de persistència; Proxy virtual al lazy loading (accedeixes acomanda.getClient()i un proxy carrega l'entitat just llavors — i llançaLazyInitializationExceptionfora de sessió: el preu del proxy);SessionFactoryés fàbrica fins i tot en el nom; la memòria cau de primer nivell funciona com un Identity Map amb gust de Flyweight: mateixa fila, mateixa instància dins de la sessió. - JUnit: el cicle
@BeforeEach→ test →@AfterEachés Template Method (a JUnit 3 era literal: heretaves deTestCasei sobreescriviessetUp()); elsTestWatcher/listeners del runner són Observer; les anotacions@ParameterizedTestamb els seus proveïdors d'arguments, fàbriques. - Android:
View/ViewGroupés el Composite de Swing reencarnat;OnClickListenerés Observer;LayoutInflater.from(context)iFragment.newInstance(...)són fàbriques;RecyclerView.Adapterés un Adapter entre les teves dades i les vistes;LiveData/Flowobservables a tot arreu. - Frontend (per al cop d'ull políglota): el patró Observer hi domina — els hooks de React i els observables de RxJS són variacions sobre "subscriure's a canvis"; Redux combina Command (accions com a objectes) amb un únic arbre d'estat. Els patrons GoF són de disseny OO, però les seves intencions creuen llenguatges.
Com reconèixer patrons en llegir codi aliè
L'habilitat pràctica: obres un repositori desconegut i vols orientar-t'hi. Pistes per ordre de fiabilitat:
- La signatura abans que el nom. Un constructor que rep un objecte del seu mateix supertipus → Decorator/Proxy/Adapter (mira si afegeix, controla o tradueix). Un mètode que rep una interfície d'un sol mètode "de fer alguna cosa" → Strategy/callback. Estàtics que retornen el propi tipus → fàbrica. Encadenables +
build()→ Builder. - L'estructura del paquet. Una interfície amb moltes implementacions germanes (
CalculEnviament+PerDistancia+TarifaPlana...) crida Strategy o State a plens pulmons; una interfície + una sola implementacióXxxImplcrida... una altra cosa (ho veurem als antipatrons). - El sufix del nom — la pista més ràpida i la menys fiable (apartat següent).
- El comportament en runtime/debug. L'stack trace mostra classes
$Proxy42oEnhancerBySpringCGLIB? Proxy dinàmic. L'objecte que recorres mai no exposa la seva col·lecció interna? Iterator fent la seva feina. - La documentació de la classe. Les llibreries serioses anomenen el patró al Javadoc ("This class implements the builder pattern") — el vocabulari compartit de 01-01 funcionant exactament com prometia.
Regla de contrast: el nom et dóna la hipòtesi; la signatura i el comportament la confirmen. Un XxxFactory que només té un getInstance() estàtic pot ser un Singleton disfressat; un XxxManager pot ser qualsevol cosa, inclòs un problema.
El que les convencions de noms ensenyen
| Sufix / prefix | Hipòtesi de patró | Exemples reals |
|---|---|---|
*Factory, *Supplier, of/valueOf/newXxx |
Factory Method / Abstract Factory | SessionFactory, ThreadFactory, List.of |
*Builder |
Builder | StringBuilder, UriComponentsBuilder |
*Adapter |
Adapter | RecyclerView.Adapter, HandlerAdapter |
*Proxy |
Proxy | java.lang.reflect.Proxy |
*Template |
Template Method (sovint amb callbacks) | JdbcTemplate, RestTemplate |
*Listener, *Observer, on*/add*Listener, subscribe |
Observer | ActionListener, @EventListener |
*Strategy, *Policy, *Resolver, Comparator |
Strategy | RetryPolicy, ViewResolver |
*Handler + camp next/successor |
Chain of Responsibility | filtres de servlet, pipelines |
*Command, *Action, *Task |
Command | accions de Redux, Runnable com a command mínim |
*Visitor, mètodes accept/visitXxx |
Visitor | FileVisitor, visitors d'ASM i dels AST |
Abstract*, Base* |
Template Method probable | AbstractList |
*Facade, *Service (a vegades), *Helper (tant de bo) |
Facade | EntityManager en esperit |
Tres lliçons que deixa aquesta taula:
- El vocabulari compartit funciona en totes dues direccions. Anomenar la teva classe
AdaptadorPayPalno és pedanteria: és documentació gratuïta que qualsevol lector amb el catàleg desxifra en un segon. És l'avantatge de comunicació de 01-06, cobrat cada dia. - El nom és un contracte. Si anomenes
ComandaFactoryuna cosa que a més envia emails, menteixes al lector amb la pitjor mena de mentida: la que sembla documentació. Anomena pel patró només quan en compleixes la intenció. - L'absència de sufix també informa. El JDK modern prefereix
List.ofaListFactory.create: quan el patró és idiomàtic, el nom del mètode basta. Els sufixos abunden on el patró necessita senyalitzar-se — i sobren on ja és cultura.
Errors Comuns i Consells
- Refiar-se només del nom.
Context,Manager,Helper,Utilno són patrons; i un*Factorypot no ser-ho. Hipòtesi pel nom, confirmació per la signatura. - Veure patrons on hi ha coincidència estructural. Que una classe n'embolcalli una altra no la fa Decorator: pregunta per la intenció (afegeix responsabilitats conservant la interfície, o controla l'accés, o tradueix?). L'acarament dels quatre embolcalls aplica també llegint codi aliè.
- Imitar la forma sense el perquè. Copiar
getInstance()d'una llibreria sense heretar-ne el problema (unicitat real) importa el cost sense el benefici. Les llibreries també arrosseguen decisions històriques:java.util.Observableestà deprecat — fins i tot el JDK des-aplica patrons mal col·locats. - Consell: quan descobreixis un patró en una llibreria, llegeix-ne el codi font (és a un clic a l'IDE). Veure com
BufferedReaderdelega en el seuReaderintern ensenya més Decorator que qualsevol diagrama. - Consell: en el teu propi codi, sigues generós amb els noms de patró quan la intenció sigui genuïna — regales al lector següent el mapa que tu vas haver de reconstruir.
Exercicis
Exercici 1: safari de patrons
Identifica el patró (i la pista que el delata) en cada fragment real:
HttpRequest.newBuilder().uri(uri).timeout(Duration.ofSeconds(5)).GET().build()Collections.unmodifiableList(comandes)— retorna unaListque llança excepció aadd.Runtime.getRuntime()new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8))Files.walkFileTree(inici, new SimpleFileVisitor<Path>() { ... })
Exercici 2: la pista enganyosa
org.springframework.core.io.ResourceLoader té un sol mètode: Resource getResource(String location), i segons el prefix (classpath:, file:, https:) retorna una implementació diferent de Resource. El seu nom diu "Loader". (a) Quin patró és realment, i quina pista pesa més que el nom? (b) Quina peça equivalent vam construir a PideYa al mòdul 2?
Exercici 3: auditoria de noms a PideYa
Repassa mentalment aquests noms del curs i digues, per a cadascun, què promet el nom al lector i si compleix la intenció del patró: FacanaCheckout, RegistreNotificadors, NotificadorAmbReintents, CentralRepartiment. Quin dels quatre és l'únic el nom del qual NO anuncia el seu patró, i per què és una decisió raonable?
Solucions
Solució 1: (1) Builder — encadenables + build(). (2) Proxy de protecció (embolcall amb la mateixa interfície que controla l'accés: no afegeix conducta, la restringeix — això el separa de Decorator). (3) Singleton clàssic del JDK — getXxx() estàtic que retorna la instància única. (4) Decorator sobre Adapter: OutputStreamWriter adapta bytes→caràcters, PrintWriter decora amb println i format — la pila de java.io. (5) Visitor (amb Template Method de regal: SimpleFileVisitor dóna implementacions per defecte que tu sobreescrius) recorrent un Composite: l'arbre de directoris.
Solució 2: (a) Factory Method: el client demana "un recurs per a aquesta ubicació" i la implementació decideix la classe concreta (ClassPathResource, FileSystemResource, UrlResource). La pista decisiva és la signatura i el comportament — un mètode que retorna una abstracció triant el subtipus per tu — no el sufix Loader. (b) El RegistreNotificadors de 02-03: mateixa idea, clau (canal / prefix) → creador registrat.
Solució 3: FacanaCheckout promet Facade i ho compleix (punt únic que orquestra un subsistema). RegistreNotificadors promet un registre de fàbriques i ho compleix (clau → Supplier). NotificadorAmbReintents no diu "Decorator"... i és l'excepció raonable: anomena allò que aporta (reintents) sobre la interfície que conserva (Notificador), que és exactament com es llegeixen els decoradors (BufferedReader tampoc no es diu ReaderDecorator); el patró es delata per la signatura — rep i exposa Notificador. CentralRepartiment tampoc no diu "Mediator", però "central" comunica la topologia en estrella millor que el nom tècnic. Moralitat: el nom ha de servir el lector; a vegades la intenció de domini comunica més que l'etiqueta del catàleg — mentre la signatura confirmi el patró.
Conclusió
El catàleg ha deixat de ser un llibre del 1994 per ser el plànol de les eines que uses cada dia: el JDK amb les seves fàbriques, builders, decoradors i iteradors; Spring com a assemblatge industrial de Singleton-per-contenidor, proxies i templates; Hibernate, JUnit i Android repetint les mateixes intencions. I t'emportes el mètode de lectura: hipòtesi pel nom, confirmació per la signatura i el comportament — amb les convencions de noms com a idioma compartit que el teu propi codi també hauria de parlar. Queda la pregunta inversa: el teu codi existent, el que no va néixer amb patrons, com es porta cap a ells sense trencar-lo? Tests com a xarxa, passos petits i un catàleg de transformacions: Refactorització Usant 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
