Design Patterns Overview
A design pattern is a named solution to a recurring problem under stated forces, together with the consequences of using it. The name is the cheap part. TechLearningPro's checkout
Design Patterns Overview
Advanced System Design & Software Architecture · Lesson 14 · Low-Level Design and Design Patterns
Lesson Overview
A design pattern is a named solution to a recurring problem under stated forces, together with the consequences of using it. The name is the cheap part. TechLearningPro's checkout squad learned the expensive part during a quarter when every pull request had to "apply a pattern." OrderFactoryFactory built an OrderFactory that built an Order the constructor could have built. DiscountStrategy had one implementation. CheckoutFacade called one service. Reviews discussed vocabulary. A change to the welcome rate still took a week, because the rate was behind a registry nobody could delete without a design meeting. The patterns had been applied as costume.
The same checkout already contained designs that deserved names. The transactional proxy in lesson 13 is a proxy: a stand-in that controls access, here to start a transaction. The port that hides a payment provider, once a second provider exists, is an adapter. DiscountPolicy from lesson 4, once a second rule exists, is a strategy. Naming those after the structure and the tests exist helps the next reader. Naming them before the second variant exists freezes a guess.
This lesson is the map and the judgment. It places the Gang of Four categories — creational, structural, behavioral — and separates them from architectural styles and from enterprise integration patterns that later lessons name. It shows the classic pattern write-up (intent, participants, consequences) applied to a small piece of TechLearningPro rather than to a toy duck simulator. It gives a selection rule you can use in an interview and in review: name a pattern when it predicts consequences you are willing to own, and delete it when the only consequence is more types. Lessons 15 through 17 implement the categories. /learn/design-patterns/what-are-patterns, /learn/design-patterns/gang-of-four, and /learn/design-patterns/patterns-not-silver-bullets are the pattern-course primers. /learn/design-patterns/when-not-to-use and /learn/design-patterns/pattern-overengineering argue the caution. Here the work is to walk a real service, point at participants, and defend or reject each name.
Learning Objectives
By the end of this lesson you will be able to:
- Define a pattern as intent plus forces plus consequences, and reject a definition that is only a class diagram copied from a poster.
- Place a problem in the creational, structural, or behavioral category, and say which lesson in this module implements it.
- Separate Gang of Four object patterns from architectural styles (layers, hexagonal) and from messaging patterns (outbox, saga) so a review does not mix their consequences.
- Write a one-page pattern note for a participant set that exists in the checkout code: intent, participants, collaborations, consequences.
- Decide that a single implementation should stay a concrete class, and record the condition that would make the pattern pay.
- Recognize Spring's own uses of proxy, template method, and a composition root without re-implementing them as hand-rolled singletons or service locators.
- Review a package for cargo-cult structure: factories of factories, strategies of one, facades that only forward.
- Explain a pattern's thread-safety, failure, and test consequences, not only its static picture.
Prerequisites
- Lessons 11 through 13: responsibility assignment, a class diagram that matches fields, and a sequence that shows commit. Patterns sit on top of those. They do not replace them.
- Lesson 4's rule that a variation point needs a scheduled second variant. This lesson uses that rule as the applicability test.
- Comfort with the words interface and composition. You do not need to have memorized twenty-three pattern names. The table below is the working set; the following lessons go deep on each.
Detailed Core Concepts
A pattern is a decision you can repeat
Christopher Alexander wrote about patterns in buildings: a problem that shows up again, a configuration that resolves the forces, and a statement of where it does not belong. The Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) carried that shape into object-oriented software in 1994. Their catalog is twenty-three patterns. The useful habit is older than the catalog and narrower than "best practice." A best practice can be "prefer constructor injection." A pattern has participants that collaborate, and consequences that include harm as well as help.
A pattern write-up in this course uses a shortened GoF form:
- Intent — the problem in one sentence, without the solution's class names.
- Applicability — when the forces are present. "We might someday" is not applicability.
- Participants — the types in this codebase, mapped to the pattern's roles.
- Collaborations — the sequence, often the one you already drew in lesson 13.
- Consequences — what becomes easier, what becomes harder, what fails at runtime.
If you cannot fill consequences with something a test or an incident could notice, you have a nickname, not a pattern. "We used Factory" is a nickname until creation is a variation point with a rule for which product appears.
The three categories
The GoF split is about what changes.
Creational patterns abstract the moment of construction. Something about new is unstable: the concrete class, the assembly steps, or the number of instances. Factory Method, Abstract Factory, Builder, Prototype, and Singleton are this category. Lesson 15 implements them in Java 21 and shows where Spring's container already did the job. A static OrderId.from(String) that checks a blank and returns a record is Bloch's static factory idiom. It is not the GoF Factory Method, which dispatches through a subclass. Interviews conflate them. This module will not.
Structural patterns compose classes and objects into larger arrangements while keeping the client's view stable. Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy are this category. Lesson 16 implements the ones checkout and payments actually force: adapter for a provider, decorator for a cross-cutting measure, facade for a subsystem entry, proxy for control of access, composite for a tree of priced parts. Bridge and flyweight are included there at the depth their forces deserve.
Behavioral patterns hand algorithms and responsibility among objects. Strategy, State, Observer, Command, Template Method, Chain of Responsibility, Iterator, Mediator, Memento, Visitor, and Interpreter are the GoF set. Lesson 17 is their home. You already met two without the titles: DiscountPolicy is strategy-shaped, and the loan status rules in lesson 11 are the seed of state. This overview names them so you can file a review comment in the right lesson. It does not re-teach the discount contract.
A fourth bucket is not in the GoF book and should not be stuffed into it. Architectural styles — layers, hexagonal ports, modular monolith — assign whole packages and deployment. They are lessons 5 through 7. Enterprise integration patterns (Hohpe and Woolf) and Fowler's Patterns of Enterprise Application Architecture name outbox, saga, repository, and unit of work. Repository is lesson 18. Saga and outbox are later, at system scale. Calling a saga a "behavioral pattern" because it is about messages confuses a process that spans databases with an object graph inside one process. Say the scale out loud.
Forces that make a pattern applicable
Use the same lens as the architecture lessons, scaled to objects:
- A real variation. A second payment provider is scheduled, or a second discount rule has a date. The pattern gives that variation a type.
- A stable client.
PlaceOrdershould not gain a branch per provider. The client is the code you are protecting. - Consequences you can operate. A singleton that hides a connection may be easy to call and hard to test, and illegal with two application contexts. Write that down.
- A failure mode. Decorator order, proxy self-invocation, builder objects left half-specified. If the pattern adds a failure the sequence did not have, the sequence must grow a fragment.
- Reversibility. Patterns that are one interface and two classes delete cleanly. Patterns that become a plugin framework do not. Prefer the former until the catalog of variants is actually large.
A pattern that satisfies none of these is cargo cult: the structure of a solution copied without the problem. The OrderFactoryFactory satisfied a naming rule and none of the forces. The payment adapter, the quarter a second provider signed a contract, satisfied the first two and had to answer the third (timeouts differ per provider, so the adapter's consequence is a shared error type or a leak of provider codes).
Participants, not posters
The GoF structure diagrams are roles. Your class diagram should use your type names and only then, if it helps, a stereotype of the role: «adapter» on CampusCardAuthorizer. Stereotyping every class is how the poster replaces the model. One stereotype on the type that plays the interesting role is enough. Lesson 12's rule still holds: the fields must match the picture. A decorator that does not hold the decorated object is not a decorator. It is a renamed service.
Collaborations are lesson 13's sequences with the new object inserted. Adding a decorator for metrics inserts a lifeline between PlaceOrder and PaymentAuthorizer. The commit arrows do not move. If they move, you have changed behavior and the pattern note must say so. A facade that starts its own transaction while the caller also has one is a collaboration bug, not a documentation nit.
Composition of patterns
Patterns compose. A builder can produce a product the factory method selects. A proxy can wrap a decorator that wraps an adapter. Composition is justified when each participant has a different force: the adapter translates a provider, the decorator records latency, the proxy enforces a deadline. Composition is cargo cult when each layer only forwards to the next and all three change whenever the provider changes. Then you have one adapter, and the other two names should go.
Spring composes patterns without asking you to restate them. The container is a composition root (an assembler, not a GoF name, but the role lesson 4 used). JdbcTemplate is a template method around connection and exception handling; you supply the statement callback. @Transactional is a proxy. Re-implementing a singleton inside a Spring bean recreates a second lifecycle the container cannot reset between tests. That collision is a consequence. It belongs in the pattern note for anyone about to add getInstance() to a @Service.
What this overview will not do
It will not walk all twenty-three implementations. That is the next three lessons plus the pattern course. It will not tell you that using a pattern is a mark of seniority. Seniority here is deleting DiscountStrategyRegistry when it wraps one class, and keeping the payment adapter when the second provider's payload actually differs.
Architecture and Design Diagrams
The category map is a filing system, not a runtime flow. Use it to choose a lesson and a review question.
mermaidflowchart TDproblem[What is unstable?]problem --> create[Construction of the object]problem --> shape[Shape of the object graph]problem --> behavior[Which algorithm or dialogue runs]problem --> scale[Process, package, or deployment boundary]create --> creational[Creational patterns lesson 15]shape --> structural[Structural patterns lesson 16]behavior --> behavioral[Behavioral patterns lesson 17]scale --> later[Architecture and integration lessons]
Checkout's real participants, with roles only where the force exists today:
mermaidclassDiagramclass PlaceOrderclass PaymentAuthorizer {<<interface>>+authorize(key, amount) Authorization}class CampusCardAuthorizer {<<adapter>>}class MeteredAuthorizer {<<decorator>>}class PlaceOrderProxy {<<proxy>>}PlaceOrder --> PaymentAuthorizerPaymentAuthorizer <|.. CampusCardAuthorizerPaymentAuthorizer <|.. MeteredAuthorizerMeteredAuthorizer --> PaymentAuthorizer : wrapsPlaceOrderProxy --> PlaceOrder : controls commit
MeteredAuthorizer is drawn only if metrics are a real requirement with a caller that must not know about the meter registry. If the use case can call a meter in two lines, the decorator is optional and the diagram should say so. CampusCardAuthorizer earns «adapter» when the provider's payload is not Authorization. One provider whose SDK already speaks your interface needs a thin client, not a ceremony. PlaceOrderProxy is Spring's transactional proxy from lesson 13, named here so the category is obvious: structural, access control, not a class you should write by hand with Proxy.newProxyInstance unless you are outside the container.
The sequence consequence of inserting the decorator is one extra hop. Commit stays where lesson 13 put it.
mermaidsequenceDiagramparticipant Place as PlaceOrderparticipant Meter as MeteredAuthorizerparticipant Card as CampusCardAuthorizerPlace->>Meter: authorize(key, amount)Meter->>Card: authorize(key, amount)alt provider timeoutCard-->>Meter: timeoutMeter-->>Place: unknownelse approvedCard-->>Meter: provider referenceMeter-->>Place: Authorizationend
The decorator's consequence is visible: it must not swallow the timeout, and it must not replace the idempotency key. A decorator that catches and returns "declined" on timeout has changed the alt guard. That is a failed collaboration, and it is the kind of consequence a pattern note exists to prevent.
Technical Explanation
Selection rule
Ask, in order:
- What change do we expect, and who asked? If nobody, stop.
- Is the unstable part construction, shape, or behavior? Pick a category. If it is the commit boundary between services, you are no longer in the GoF catalog.
- Is there a GoF pattern whose intent matches, and whose consequences we accept? Read the consequences from the book or from this module, not from the class diagram alone.
- What is the smallest participant set? Usually an interface the client already should depend on, plus one class per variant.
- What test locks the collaboration? A decorator test that the inner authorizer saw the same key. A factory test that an unknown format fails at the edge, not inside the exporter.
- What will we delete if the second variant is cancelled? If the answer is "nothing, the framework is the design," you have overbuilt.
Static factory methods (List.of, Instant.parse, Money compact constructors) sit beside this rule as ordinary Java. Use them for named constructors and caching of immutables. Do not draw a Factory Method diagram for List.of. You will confuse an interviewer who asks about subclass-selected products, and you will confuse yourself when you read lesson 15.
Spring is allowed to be the mechanism
Several forces the GoF solved with hand-written structure are container features now.
| Force | Hand-rolled GoF move | What you do on Boot 3 |
|---|---|---|
| One shared collaborator per process | Singleton with getInstance | A singleton-scoped bean, the default |
| Choose an implementation at startup | Abstract factory of your own | A @Bean method, sometimes @Profile or @ConditionalOnProperty |
| Wrap a call with a transaction or a deadline | Hand-written proxy | @Transactional, or a client timeout on the HTTP call |
| Hide JDBC boilerplate | Template method you own | JdbcClient or JdbcTemplate, already a template |
| Obtain a dependency without a constructor | Service locator | Constructor injection. A locator is the consequence you are refusing |
FactoryBean is a Spring SPI, not a GoF factory. Lesson 15 treats it carefully. The overview rule is: do not implement FactoryBean to return a class you could have constructed in a @Bean method. The indirection hides the product type in the container and produces the &beanName surprise.
Consequences worth writing down
Every accepted pattern in checkout gets four lines in the decision record lesson 10 taught you to write:
- Intent, in domain words ("translate the campus card payload into
Authorization"). - Participants, with type names.
- The failure that stays local (provider timeout becomes
unknown, not a 201). - The test that fails if a new participant drops the idempotency key.
A pattern without a test is a drawing. The cargo-cult registry had diagrams and no test that a rate change touched one class. The incident was the missing consequence line: "a rate edit crosses the registry, the combinator, and the use case."
Security and operations
Patterns move trust boundaries. An adapter is the place a provider's raw status string must die, so PlaceOrder cannot be tricked into treating "APPROVED_TEST" as approved by a string prefix. A facade that logs the full provider payload may log a card number. The facade's consequence includes a redaction list. A singleton cache of member entitlements is shared across requests and is a data-leak bug if the cache key omits the member id. Creational shortcuts have security consequences. Say them in the same note as the happy structure.
Observability follows the decorator or the listener you already have. A new pattern that catches exceptions to "keep the flow clean" erases the span. The collaboration must record the failure the sequence named. Patterns are not allowed to improve the diagram by deleting the alt operand.
Practical Implementation or Design Walkthrough
Problem
Walk the existing checkout and lending types. Name the patterns whose forces are present. Remove or refuse the names whose forces are not. Leave the code smaller than a pattern catalog.
Inventory
| Type you have | Force present? | Name if yes | Action if no |
|---|---|---|---|
PlaceOrder called through Spring's transactional proxy | Yes: commit must wrap the method and not the caller's HTTP write | Proxy | Do not hand-write a proxy |
PaymentAuthorizer with one HTTP provider whose JSON is not your type | Yes, once the payload differs | Adapter | Keep the translation in one class |
| Metrics around authorize | Only if more than one caller must be metered the same way | Decorator | Otherwise three lines in the adapter |
DiscountPolicy with one percent | No second rule | None | Concrete class; reopen when the second rule is scheduled |
DailyRupeeFine | One formula | None | Same decision as lesson 11 |
Order constructor checks | Yes: invalid orders must be hard to build | Not a GoF pattern | Compact constructor or place factory method on the type |
Proposed CheckoutFacade | It would only call PlaceOrder | None | The use case is already the entry |
The table is the design. The implementation below keeps the adapter, because the campus card processor returns a status string and a reference, and PlaceOrder must see Authorization only. It refuses a strategy hierarchy for a single discount. It records both decisions.
The adapter that earns the name
The provider contract we depend on is small and owned by our tests: a JSON object with status (APPROVED, DECLINED, UNKNOWN) and reference. We do not pretend this is a particular vendor SDK. The adapter is the only class allowed to read those strings.
javapackage com.techlearningpro.checkout;import java.util.Objects;import org.springframework.web.client.ResourceAccessException;import org.springframework.web.client.RestClient;public final class CampusCardAuthorizer implements PaymentAuthorizer {private final RestClient http;public CampusCardAuthorizer(RestClient http) {this.http = Objects.requireNonNull(http);}@Overridepublic Authorization authorize(PaymentKey key, Money amount) {CardRequest body = new CardRequest(key.value(), amount.amount(), amount.currency());CardResponse response;try {response = http.post().uri("/v1/authorizations").body(body).retrieve().body(CardResponse.class);} catch (ResourceAccessException ex) {return Authorization.unknown("timeout");}if (response == null || response.status() == null) {return Authorization.unknown("empty");}return switch (response.status()) {case "APPROVED" -> Authorization.approved(response.reference());case "DECLINED" -> Authorization.declined(response.reference());default -> Authorization.unknown(response.status());};}public record CardRequest(String idempotencyKey, java.math.BigDecimal amount, String currency) {}public record CardResponse(String status, String reference) {}}
RestClient has been in Spring Framework since 6.1. ResourceAccessException is the Spring wrapper for I/O failures on the client, including timeouts when the client is configured with one. The switch is the translation. An unknown status is unknown, not approved. That is the security consequence: a new provider string must not become a successful charge because someone used startsWith("APP").
PlaceOrder from lesson 13 depends on PaymentAuthorizer. It does not depend on CardResponse. That dependency direction is the adapter's structural point. Lesson 16 will compare it with facade and with a second provider. This overview only checks that the name is earned.
The pattern note
Intent: translate the campus card HTTP payload into Authorization without letting provider strings into the use case.
Participants: PaymentAuthorizer (target), CampusCardAuthorizer (adapter), RestClient (adaptee's transport).
Collaborations: the sequence in the previous section. Timeout becomes unknown. The idempotency key is copied onto CardRequest unchanged.
Consequences: a new status code is a one-line change in the adapter and a test. A second provider is a second class and a @Bean choice, not an edit to PlaceOrder. The cost is a type that must stay the only parser of status. If a future change parses status in the controller "just for logging," the consequence is void.
Refused: DiscountStrategy and CheckoutFacade and a hand-rolled singleton for RestClient. RestClient is a bean. The discount remains WelcomePercentDiscount until a second rule is dated. The facade remains unwritten because PlaceOrder.place is already one method.
Wiring without a factory of factories
java@Configurationclass CheckoutBeans {@BeanPaymentAuthorizer authorizer(RestClient campusCard) {return new CampusCardAuthorizer(campusCard);}@BeanPlaceOrder placeOrder(OrderStore orders, PaymentAuthorizer authorizer,ApplicationEventPublisher events) {return new PlaceOrder(orders, authorizer, events);}}
The @Bean methods are the composition root. They are not an Abstract Factory. There is one product family. If PDF and HTML exporters later need matched pairs of header and body writers, that is lesson 15's abstract factory, and it will have to pass the applicability test again. Do not "upgrade" this @Bean into a family of factories in advance.
A test that the consequence holds
java@Testvoid unknownStatusIsNotApproval() {// RestClient stubbed to return status "APPROVED_TEST"PaymentAuthorizer authorizer = stubbedAuthorizer("APPROVED_TEST");Authorization result = authorizer.authorize(new PaymentKey("o-1"), new Money(new BigDecimal("10.00"), "USD"));assertFalse(result.approved());assertEquals(Authorization.Kind.UNKNOWN, result.kind());}
The stub is local. The point of the test is the switch's default arm. A cargo-cult adapter with no test would map any unrecognized value to approved "so checkout does not break." That is the consequence the pattern note forbids. Breaking checkout closed is better than charging.
Enterprise Real-World Scenarios
The quarter the vocabulary was the design
The institute's architecture guild required a pattern name in every design summary. Engineers complied. Checkout gained a factory for orders, a factory for the factory because "creation should be open for extension," a strategy with a single percent, a facade that logged and called PlaceOrder, and a singleton ConfigHolder beside Spring's environment. A rate change for the welcome offer touched the strategy, the registry, the facade's log format, and a test that constructed the stack in a custom order. The diff was twelve files. The guild's checklist was satisfied on every file.
The reversal took a week and a decision record. We kept CampusCardAuthorizer and the transactional proxy, because their forces were real and their tests failed if translation or commit moved. We deleted the factory pair, the registry, the facade, and ConfigHolder. @Configuration plus constructor injection replaced them. The next rate change was one class. The guild updated the checklist: a pattern name must be followed by the force and the consequence, or the name comes out. That is the whole policy this lesson is willing to impose.
The adapter that was promoted too late
A second provider, the city transit card, was added by if (campus) ... else ... inside PlaceOrder. The branch knew two JSON shapes. A transit status of OK was interpreted with the campus switch and fell through to unknown, so transit payments never completed. The repair was a second adapter, TransitCardAuthorizer, same PaymentAuthorizer interface, chosen with @ConditionalOnProperty on the bean. PlaceOrder returned to one call. The pattern was late, and the cost of being late was a week of failed charges, not a week of extra interfaces. Applicability had been true the day the contract was signed. Waiting for a third provider would have been the cargo cult in the other direction: refusing a name whose force is standing in the room.
The lesson is not "always abstract" or "never abstract." It is "write the force down when it becomes true, and do the smallest participant set that day." Two adapters and one interface was the set. A plugin loader was discussed and rejected. The reopen condition is a provider that must be added without a deploy, which this business does not need.
Best Practices
- Write intent and consequences in domain words. Attach the GoF name afterward if it helps a reader search.
- Map participants to types you have. Stereotype one role, not every class.
- Keep the sequence from lesson 13 true when you insert a participant. Commit, idempotency key, and timeout guards stay.
- Prefer a concrete class for one variant. Record the reopen condition next to it.
- Let Spring's container,
JdbcTemplateorJdbcClient, and@Transactionalbe the mechanisms they are. Do not re-implement them as singletons, locators, or hand-rolled proxies. - Delete a pattern that has one implementation and no dated second. Deletion is a successful design review.
- Compose patterns only when each layer has a different force and a different test.
- Put provider vocabulary inside the adapter. Unknown strings fail closed.
- When the unstable thing is a process across services, leave the GoF catalog and use the later architecture lessons.
- In an interview, say the category, the force, and one consequence you refuse. Then stop talking about patterns and show the code.
Common Mistakes and Anti-Patterns
The costume. A pattern name on a class that forwards to one collaborator. Reviews debate the name. The rate change is still hard. Delete the type or give it a force and a test.
Factory of factories. OrderFactoryFactory so creation is "extensible" when Order.place already guards the invariant. Extra types, no variation. Construction belongs on the type that knows the rule.
Strategy of one. An interface, a registry, and a single percent. The next edit is slower than a constant. Keep the concrete class until the second rule is scheduled.
Facade as a god use case. One type that prices, charges, emails, and enrolls, labeled facade because the word sounds like an entry point. A facade simplifies a subsystem for a client. A use case that owns a transaction is a use case. Name it for the action (PlaceOrder).
Hand-rolled singleton beside Spring. getInstance() state shared across tests and across two contexts in one JVM. Use a bean. Lesson 15 shows the few times a true singleton object is still justified, and how to make it safe. The default is the container.
Pattern as a substitute for a sequence. A beautiful adapter that maps timeout to declined, quietly, because the class diagram had no place for the guard. The collaboration is part of the pattern. The alt fragment stays.
Catalog mixing. Calling the modular monolith a "composite pattern," or the outbox a "memento." The consequences are unrelated. Use the scale-correct name so the next reader opens the right lesson.
Fail-open translation. Any unrecognized provider status becomes approved so the demo works. The adapter's job is a closed mapping. Unknown stays unknown.
Troubleshooting and Design Review
Symptom: A one-line business change touches a registry, a facade, and the use case.
Likely cause: a pattern was introduced without a second variant, and the real edit is not one of the extension points.
Investigation: count classes that mention the constant. Read the applicability line in the design note. If there is no note, that is the finding.
Corrective action: collapse to the concrete type. Keep the adapter or proxy only if its test still describes a force you have.
Symptom: A new provider status charges customers.
Likely cause: translation uses a prefix, or parsing escaped the adapter into the controller.
Investigation: search for the status string. The only production hits should be inside the adapter and its tests.
Corrective action: an exhaustive mapping whose default is unknown. Add the test from this lesson with a lookalike status.
Symptom: Two application contexts in one test JVM corrupt each other's "singleton."
Likely cause: a GoF singleton or a static cache beside Spring beans.
Investigation: static mutable fields in the checkout package.
Corrective action: container scope, or an explicit store. Do not add synchronization to the static as the first fix. The static is the defect.
Symptom: Reviews last an hour and never mention commit or the idempotency key.
Likely cause: the checklist asks for pattern names first.
Investigation: the design summary template.
Corrective action: require force, participants, and one consequence. Make the sequence a required attachment. Names are optional.
Symptom: FactoryBean or a custom Proxy appears in a domain package.
Likely cause: the container feature was re-implemented where a @Bean or @Transactional would do.
Investigation: whether getObject does anything a @Bean method could not.
Corrective action: delete the SPI implementation. Lesson 15 lists the rare cases (integrating a library that must be constructed by a callback) where FactoryBean remains.
Review questions: What force is true today? What is the participant set, and can we delete one? Which sequence guard can this pattern violate? What did we refuse to name, and when do we reopen? Is this a GoF object pattern or an architectural style wearing the wrong label?
Interview Questions and Answers
Q1. What is a design pattern, in a sentence you would defend?
A named way to arrange objects for a recurring problem, with explicit consequences, used when those forces are present. The class diagram is not the pattern. I include what gets worse: more types, a new failure mode, a test I must add. If I cannot name a consequence, I do not use the name in the review.
Q2. How do the three GoF categories differ?
Creational patterns vary what is constructed and how. Structural patterns vary how objects are composed while the client sees a stable shape. Behavioral patterns vary which algorithm or dialogue runs. I file a payment-payload translation as structural (adapter), a choice of discount rule as behavioral (strategy), and a multi-step document assembly as creational (builder). A saga that spans services is none of the three. It is an integration pattern at a different scale.
Q3. When do you refuse a pattern even though it "fits" the diagram?
When there is a single variant and no dated second, or when the pattern's consequence fights the sequence we already committed to. One discount is a class. A decorator that maps timeout to success makes the diagram prettier and the charges wrong. I also refuse a pattern whose problem Spring already solves, such as a hand-rolled singleton for a stateless service. The container's singleton scope is the mechanism. The GoF singleton object would add global state.
Q4. What is the difference between a static factory method and the Factory Method pattern?
A static factory is a method on a type, such as Instant.parse or a compact constructor's checks, that returns an instance and has nowhere to plug in a subclass. GoF Factory Method lets a subclass decide which product to instantiate, so the caller depends on the creator abstraction. I use static factories constantly and Factory Method rarely. Saying "factory" without that distinction fails the interview question even if the code compiles.
Q5. How does a pattern show up on a sequence diagram?
As a lifeline whose messages preserve the guards you already needed. An adapter translates and returns unknown on timeout. A proxy starts and completes the transaction around the use case. If the new lifeline deletes a guard, the pattern application is wrong. I would rather keep an unnamed class that preserves the alt operands than a named one that folds them into a single success return.
Q6. What did the cargo-cult quarter get wrong, and what did it get right if anything?
It required names without forces, so factories and a one-implementation strategy multiplied the diff for a rate change. The transactional boundary and the provider translation were real and should have kept their names. The repair was deletion plus a checklist that demands a consequence. I tell this story because "patterns are good" and "patterns are bad" are both incomplete. Applicability decides.
Q7. How do you decide the pattern is too small or too large?
Too small: the participant only forwards, and a method on the existing type would have the same call sites. Too large: the "pattern" is actually a plugin framework, a process manager, or a service boundary. I want the smallest set that localizes one change. Two adapters and one interface localized the transit-card bug. A provider plugin loader would have been a new product.
Q8. Where do repository, unit of work, and saga sit relative to the GoF?
Repository and unit of work are enterprise application patterns about persistence. Lesson 18 covers them. Saga and outbox are integration and consistency patterns across transactions. They use objects, and they are not "the command pattern" merely because a message is a request. I mention the relationship when an interviewer mixes the catalogs, and I answer at the scale they asked about.
Q9. What test do you add when you accept an adapter?
A mapping test: approved, declined, a lookalike status, a null body, and a transport timeout, asserting Authorization kind and that the idempotency key left our process unchanged. I do not add a test that the class "is an adapter." I add a test that the consequence holds. If the use case test starts parsing provider JSON, the participant boundary has already leaked.
Q10. How do you talk about patterns in a 45-minute LLD interview?
I design the types and the sequence first, as in lessons 11 through 13. I name a pattern only when I introduce a participant for a force the interviewer confirmed, and I state one consequence. I do not open with a catalog. If they ask "which pattern is this?", I answer with category and intent, then return to the failure operand. A parking lot or a checkout does not need all twenty-three names. It needs ownership, a transaction boundary, and one variation point that is real.
Q11. What do you do with a pattern that the team already misapplied?
I identify which forces are still true. I keep those participants and their tests. I delete the rest in the same change if I can do it safely, or I write the deletion as the next change with the reopen condition left on the concrete class. I do not add a third wrapper to "fix" a facade that should not exist. The quarter we did this, the rate-change diff collapsed, and that was the measure.
Hands-On Exercises
Exercise: Name the patterns that are actually there
Problem statement. You are given a checkout package that contains PlaceOrder, a single percent discount class, a proposed CheckoutFacade, a ConfigHolder.getInstance(), and a campus card HTTP client that returns raw status strings. Decide which pattern names to keep, which code to delete, and which name to add.
Tasks.
- List each type, the force that would justify a pattern, and whether that force is true.
- Draw a class diagram with at most one stereotype, on the adapter or the proxy.
- Draw the authorize sequence including timeout, and show that a decorator or facade does not erase
unknown. - Implement the status mapping so a lookalike status is not approval. Use
RestClientor a narrow interface you define; do not invent a vendor SDK. - Write the four-line pattern note for the adapter and the refusal note for the discount strategy.
- Show the
@Beanmethods that replaceConfigHolderand any factory-of-factories. - State one security consequence of parsing provider strings outside the adapter.
- Say which category you would open next if a second discount rule were scheduled (behavioral, lesson 17) and why it is not creational.
Expected deliverables. The inventory table, two diagrams, the adapter and its mapping test, the pattern note, and the deletion list.
Evaluation rubric.
| Assessment area | Weight | What "good" looks like |
|---|---|---|
| Applicability | 25% | Names track forces; single discount stays concrete |
| Consequences | 20% | Timeout and unknown status stay fail-closed |
| Participant fit | 15% | Adapter translates; proxy is Spring's, not hand-rolled |
| Sequence | 15% | Commit and the key survive the new lifeline |
| Deletion | 15% | Facade, holder, and extra factories are removed or refused in writing |
| Catalog hygiene | 10% | GoF versus architecture versus integration is not mixed |
A submission that adds a pattern and does not delete one, with a single provider and a single discount, has missed the point of the overview.
Lesson Summary
Patterns name recurring object structures and the consequences that come with them. The GoF categories file construction, shape, and behavior. Architectural styles and integration patterns file larger scales and should keep their own names. In checkout, the transactional proxy and the campus-card adapter earn their names because the forces are present and the tests can see the consequences. A strategy of one, a facade that only forwards, and a singleton beside Spring do not. The selection rule is a real change, a stable client, a failure mode you will still handle, and a participant set you can delete. Lessons 15 and 16 take the creational and structural categories into Java and Spring in that spirit: idioms where they are enough, patterns where the force is dated, and container features left in the container.
Further Reading
- Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns — intent, applicability, participants, and consequences. Read the consequence sections. They are the part posters omit.
- Christopher Alexander, A Pattern Language — the form the GoF adapted. You do not need it to pass an interview. You need the idea that a pattern says where it does not apply.
- Joshua Bloch, Effective Java — static factories, builders, and the enum singleton, which lesson 15 will use as Java idioms beside the GoF names.
- Martin Fowler, Patterns of Enterprise Application Architecture — repository and unit of work, so you can tell them from the GoF catalog. Lesson 18 is the local treatment.
- Gregor Hohpe and Bobby Woolf, Enterprise Integration Patterns — the messaging catalog. Not a substitute for adapter versus facade.
- Spring Framework, FactoryBean — the SPI this overview told you not to reach for first. Lesson 15 expands the caution.
- Primers: /learn/design-patterns/selection-framework, /learn/design-patterns/when-not-to-use, /learn/design-patterns/pattern-overengineering, /learn/low-level-design/software-design-process.
- This course: /learn/advanced-system-design/creational-design-patterns, /learn/advanced-system-design/structural-design-patterns, /learn/advanced-system-design/behavioral-design-patterns, /learn/advanced-system-design/dependency-injection-and-inversion-of-control.