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

  1. Patrons al JDK
  2. Patrons a Spring
  3. Altres llibreries i frameworks
  4. Com reconèixer patrons en llegir codi aliè
  5. El que les convencions de noms ensenyen
  6. 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, sense getInstance() estàtic, sense estat global intocable en tests. La ConfiguracioPideYa de PideYa va acabar així.
  • Factory pertot arreu — BeanFactory/ApplicationContext són fàbriques gegants; la interfície FactoryBean<T> et deixa escriure la teva; @Bean en una classe @Configuration és un Factory Method que el contenidor invoca per tu.
  • Proxy dinàmic com a columna vertebral — @Transactional, @Cacheable, @Async i 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 @Transactional no 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'ObservadorComanda de PideYa (amb bonus: @TransactionalEventListener per 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 HandlerAdapter de Spring MVC adapten tipus de controlador heterogenis; DispatcherServlet és la façana de tot el processament web.

Altres llibreries i frameworks

  • Hibernate / JPA: Session/EntityManager com a Facade del motor de persistència; Proxy virtual al lazy loading (accedeixes a comanda.getClient() i un proxy carrega l'entitat just llavors — i llança LazyInitializationException fora 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 de TestCase i sobreescrivies setUp()); els TestWatcher/listeners del runner són Observer; les anotacions @ParameterizedTest amb els seus proveïdors d'arguments, fàbriques.
  • Android: View/ViewGroup és el Composite de Swing reencarnat; OnClickListener és Observer; LayoutInflater.from(context) i Fragment.newInstance(...) són fàbriques; RecyclerView.Adapter és un Adapter entre les teves dades i les vistes; LiveData/Flow observables 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:

  1. 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.
  2. 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ó XxxImpl crida... una altra cosa (ho veurem als antipatrons).
  3. El sufix del nom — la pista més ràpida i la menys fiable (apartat següent).
  4. El comportament en runtime/debug. L'stack trace mostra classes $Proxy42 o EnhancerBySpringCGLIB? Proxy dinàmic. L'objecte que recorres mai no exposa la seva col·lecció interna? Iterator fent la seva feina.
  5. 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 AdaptadorPayPal no é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 ComandaFactory una 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.of a ListFactory.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, Util no són patrons; i un *Factory pot 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.Observable està 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 BufferedReader delega en el seu Reader intern 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:

  1. HttpRequest.newBuilder().uri(uri).timeout(Duration.ofSeconds(5)).GET().build()
  2. Collections.unmodifiableList(comandes) — retorna una List que llança excepció a add.
  3. Runtime.getRuntime()
  4. new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8))
  5. 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

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