Throughout the course we have built patterns on top of PideYa; this lesson flips the telescope around: the patterns you have been using for years without knowing it. Every time you traverse a list with a for-each, wrap a stream in a BufferedReader or let Spring inject a bean for you, you are consuming Iterator, Decorator and Singleton written by others. Reading these examples has double value: they confirm that the catalog is not academic theory but the engineering behind the world's most-used libraries, and they train the most profitable skill of this lesson — recognizing a pattern while reading someone else's code from its clues, starting with the names. We won't talk about architecture here (layers, microservices, messaging): that is module 6 territory; here we look at concrete classes and APIs.

Contents

  1. Patterns in the JDK
  2. Patterns in Spring
  3. Other libraries and frameworks
  4. How to recognize patterns when reading someone else's code
  5. What naming conventions teach
  6. Exercises and conclusion

Patterns in the JDK

Java's standard library is the largest catalog of patterns in production on the planet:

Pattern Where it lives in the JDK The clue
Iterator java.util.Iterator, Iterable, the whole for-each The pattern was promoted to language syntax
Decorator java.io: BufferedReader, GZIPOutputStream... Constructors that receive another stream of the same type
Observer Swing listeners (ActionListener), java.util.concurrent.Flow addXxxListener, subscribe
Factory Method Integer.valueOf, List.of, Optional.of, Files.newBufferedReader Statics that return the type... or a subtype you don't get to choose
Builder StringBuilder, HttpRequest.newBuilder(), Stream.Builder Chainable methods + a final build()
Proxy java.lang.reflect.Proxy (the foundation of half the ecosystem) The JDK ships the pattern as a built-in utility
Strategy Comparator in sort, ThreadFactory, UncaughtExceptionHandler The algorithm travels as an argument
Template Method AbstractList, InputStream.read(byte[]) on top of read(), ClassLoader.loadClass An Abstract* class with methods to "fill in"
Composite Swing: Container is a Component that contains Components The container implements the interface of the content
Flyweight The Integer.valueOf cache (−128..127), the string pool Shared instances for repeated values
Adapter Arrays.asList, Collections.list(Enumeration), InputStreamReader Converts one interface (array, Enumeration, bytes) into another (List, Iterator, chars)
Prototype Object.clone() / Cloneable The mechanism exists... with the caveats we saw in 02-06

The java.io case deserves a zoom-in, because it is the same stacking as PideYa's dish extras:

// Three decorators stacked on a core, composable in any reasonable order:
var reader = new BufferedReader(                    // adds buffering and readLine()
                 new InputStreamReader(             // adapts bytes → characters (an Adapter in the stack!)
                     new FileInputStream("orders.csv")));   // the concrete component

// And the head-to-head, live: Integer.valueOf is Factory Method WITH Flyweight inside
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
// a == b is true: valueOf returned the SAME cached instance (-128..127).
// With new Integer(100) (deprecated) they would be distinct objects: the factory
// exists precisely so that decisions like this can be made without the client knowing.

That last comment is module 2's entire creational moral (02-07): whoever controls creation controls sharing, caching and the subtype — which is why the modern JDK (List.of, Optional.of) barely lets you write new anymore.

Patterns in Spring

Spring doesn't "use" patterns: it is patterns assembled, and several of them resolve critiques we made during the course:

  • Singleton per container — a bean's default scope: one instance... per ApplicationContext, not per JVM. It is exactly the alternative we defended in 02-02: uniqueness managed by whoever injects, no static getInstance(), no untouchable global state in tests. PideYa's PideYaConfig ended up this way.
  • Factories everywhere — BeanFactory/ApplicationContext are giant factories; the FactoryBean<T> interface lets you write your own; @Bean in a @Configuration class is a Factory Method the container invokes for you.
  • Dynamic Proxy as the backbone — @Transactional, @Cacheable, @Async and method security work because Spring wraps your bean in a proxy (JDK dynamic if there is an interface, CGLIB otherwise) that intercepts the call, does its magic and delegates. It is module 3's Proxy industrialized — and it explains the classic "my @Transactional doesn't work when I call myself": the self-call never goes through the proxy.
  • Template Method without inheritance — JdbcTemplate, RestTemplate, TransactionTemplate: the skeleton (open connection, execute, map, close, translate exceptions) is fixed; your variable step comes in as a lambda/callback. The composition-based variant from the final exercise of 04-11:
// The template fixes the flow (connection, statement, loop, cleanup, exceptions);
// you only contribute the variable step: how to map a row.
List<Order> orders = jdbcTemplate.query(
    "SELECT * FROM orders WHERE status = ?",
    (row, num) -> new Order(row.getLong("id"), row.getString("status")), // your "hook"
    "OUT_FOR_DELIVERY");
  • Observer in the events — ApplicationEventPublisher.publishEvent(...) + @EventListener: broadcast to anonymous interested parties, identical in intent to PideYa's OrderObserver (with a bonus: @TransactionalEventListener to listen "once the commit is real").
  • Strategy institutionalized — injecting an interface with N implementations (an autowired List<DiscountRule>, or choosing with @Qualifier) is Strategy with the container as the selector.
  • Internal Adapters and Facades — Spring MVC's HandlerAdapters adapt heterogeneous controller types; DispatcherServlet is the facade over all web processing.

Other libraries and frameworks

  • Hibernate / JPA: Session/EntityManager as a Facade over the persistence engine; a virtual Proxy in lazy loading (you touch order.getCustomer() and a proxy loads the entity right then — and throws LazyInitializationException outside a session: the price of the proxy); SessionFactory is a factory down to its name; the first-level cache works like an Identity Map with a Flyweight flavor: same row, same instance within the session.
  • JUnit: the @BeforeEach → test → @AfterEach cycle is Template Method (in JUnit 3 it was literal: you extended TestCase and overrode setUp()); the runner's TestWatcher/listeners are Observer; @ParameterizedTest annotations with their argument providers, factories.
  • Android: View/ViewGroup is Swing's Composite reincarnated; OnClickListener is Observer; LayoutInflater.from(context) and Fragment.newInstance(...) are factories; RecyclerView.Adapter is an Adapter between your data and the views; observable LiveData/Flow everywhere.
  • Frontend (for the polyglot glance): the Observer pattern dominates — React hooks and RxJS observables are variations on "subscribe to changes"; Redux combines Command (actions as objects) with a single state tree. GoF patterns are OO design patterns, but their intents cross languages.

How to recognize patterns when reading someone else's code

The practical skill: you open an unfamiliar repository and want to get your bearings. Clues in order of reliability:

  1. The signature before the name. A constructor that receives an object of its own supertype → Decorator/Proxy/Adapter (check whether it adds, controls or translates). A method that receives a single-method "do something" interface → Strategy/callback. Statics returning their own type → factory. Chainables + build() → Builder.
  2. The package structure. An interface with many sibling implementations (DeliveryFeeCalculation + DistanceFee + FlatRateFee...) screams Strategy or State; an interface + a single XxxImpl implementation screams... something else (we'll see it in anti-patterns).
  3. The name's suffix — the fastest clue and the least reliable (next section).
  4. Runtime/debug behavior. Does the stack trace show $Proxy42 or EnhancerBySpringCGLIB classes? Dynamic proxy. Does the object you traverse never expose its internal collection? Iterator doing its job.
  5. The class documentation. Serious libraries name the pattern in the Javadoc ("This class implements the builder pattern") — the shared vocabulary from 01-01 working exactly as promised.

Cross-check rule: the name gives you the hypothesis; the signature and behavior confirm it. An XxxFactory whose only method is a static getInstance() may be a Singleton in disguise; an XxxManager can be anything, including a problem.

What naming conventions teach

Suffix / prefix Pattern hypothesis Real examples
*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 (often with callbacks) JdbcTemplate, RestTemplate
*Listener, *Observer, on*/add*Listener, subscribe Observer ActionListener, @EventListener
*Strategy, *Policy, *Resolver, Comparator Strategy RetryPolicy, ViewResolver
*Handler + a next/successor field Chain of Responsibility servlet filters, pipelines
*Command, *Action, *Task Command Redux actions, Runnable as a minimal command
*Visitor, accept/visitXxx methods Visitor FileVisitor, ASM and AST visitors
Abstract*, Base* Likely Template Method AbstractList
*Facade, *Service (sometimes), *Helper (one hopes) Facade EntityManager in spirit

Three lessons this table leaves behind:

  • The shared vocabulary works in both directions. Naming your class PayPalAdapter is not pedantry: it is free documentation that any reader who knows the catalog deciphers in a second. It is the communication advantage from 01-06, cashed in every day.
  • The name is a contract. If you call something OrderFactory and it also sends emails, you are lying to the reader with the worst kind of lie: the kind that looks like documentation. Name after the pattern only when you honor its intent.
  • The absence of a suffix is also information. The modern JDK prefers List.of to ListFactory.create: when the pattern is idiomatic, the method name is enough. Suffixes abound where the pattern needs signposting — and are redundant where it is already culture.

Common Mistakes and Tips

  • Trusting the name alone. Context, Manager, Helper, Util are not patterns; and an *Factory may not be one either. Hypothesis by name, confirmation by signature.
  • Seeing patterns where there is only structural coincidence. One class wrapping another doesn't make it a Decorator: ask about the intent (does it add responsibilities while preserving the interface, or control access, or translate?). The head-to-head of the four wrappers applies to reading other people's code too.
  • Imitating the form without the why. Copying a library's getInstance() without inheriting its problem (genuine uniqueness) imports the cost without the benefit. Libraries drag historical decisions too: java.util.Observable is deprecated — even the JDK un-applies badly placed patterns.
  • Tip: when you discover a pattern in a library, read its source code (it's one click away in the IDE). Watching BufferedReader delegate to its inner Reader teaches more Decorator than any diagram.
  • Tip: in your own code, be generous with pattern names when the intent is genuine — you hand the next reader the map you had to reconstruct yourself.

Exercises

Exercise 1: pattern safari

Identify the pattern (and the clue that gives it away) in each real fragment:

  1. HttpRequest.newBuilder().uri(uri).timeout(Duration.ofSeconds(5)).GET().build()
  2. Collections.unmodifiableList(orders) — returns a List that throws on add.
  3. Runtime.getRuntime()
  4. new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), UTF_8))
  5. Files.walkFileTree(start, new SimpleFileVisitor<Path>() { ... })

Exercise 2: the misleading clue

org.springframework.core.io.ResourceLoader has a single method: Resource getResource(String location), and depending on the prefix (classpath:, file:, https:) it returns a different implementation of Resource. Its name says "Loader". (a) Which pattern is it really, and which clue outweighs the name? (b) Which equivalent piece did we build in PideYa in module 2?

Exercise 3: name audit in PideYa

Go over these course names in your head and say, for each one, what the name promises the reader and whether it honors the pattern's intent: CheckoutFacade, NotifierRegistry, RetryNotifier, DispatchCenter. Which of the four is the only one whose name does NOT announce its pattern, and why is that a reasonable decision?

Solutions

Solution 1: (1) Builder — chainables + build(). (2) Protection Proxy (a wrapper with the same interface that controls access: it doesn't add behavior, it restricts it — that is what separates it from Decorator). (3) The JDK's classic Singleton — a static getXxx() returning the single instance. (4) Decorator on top of Adapter: OutputStreamWriter adapts bytes→characters, PrintWriter decorates with println and formatting — the java.io stack. (5) Visitor (with Template Method thrown in: SimpleFileVisitor provides default implementations you override) traversing a Composite: the directory tree.

Solution 2: (a) Factory Method: the client asks for "a resource for this location" and the implementation decides the concrete class (ClassPathResource, FileSystemResource, UrlResource). The decisive clue is the signature and the behavior — a method returning an abstraction while choosing the subtype for you — not the Loader suffix. (b) The NotifierRegistry from 02-03: same idea, key (channel / prefix) → registered creator.

Solution 3: CheckoutFacade promises Facade and delivers (a single point orchestrating a subsystem). NotifierRegistry promises a registry of factories and delivers (key → Supplier). RetryNotifier doesn't say "Decorator"... and it is the reasonable exception: it names what it contributes (retries) on top of the interface it preserves (Notifier), which is exactly how decorators read (BufferedReader isn't called ReaderDecorator either); the pattern gives itself away through the signature — it receives and exposes a Notifier. DispatchCenter doesn't say "Mediator" either, but "center" communicates the star topology better than the technical name would. The moral: the name must serve the reader; sometimes the domain intent communicates more than the catalog label — as long as the signature confirms the pattern.

Conclusion

The catalog stopped being a book from 1994 and became the blueprint of the tools you use daily: the JDK with its factories, builders, decorators and iterators; Spring as an industrial assembly of Singleton-per-container, proxies and templates; Hibernate, JUnit and Android repeating the same intents. And you take away the reading method: hypothesis from the name, confirmation from the signature and the behavior — with naming conventions as the shared language your own code should speak too. One inverse question remains: your existing code, the code that wasn't born with patterns — how do you carry it toward them without breaking it? Tests as the net, small steps and a catalog of transformations: Refactoring with Design Patterns.

© Copyright 2026. All rights reserved