Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
Chaturmind
← Java Interview Prep: 8+ Years (Senior & Lead)

Expert Core Java

  • Tricky Java Output, Operators & OOP Edge Cases — Interview Questions
  • Tricky Exceptions, Memory & Keyword Questions — Interview Questions
  • Classic Java Language Questions, Senior-Grade Answers — Interview Questions
  • Classic Collections, Threads & JDK APIs, Senior-Grade Answers — Interview Questions
  • Reflection, Dynamic Proxies, final & Modern OOP Design — Interview Questions

JVM Internals & Performance

  • Class Loading, Bytecode & Object Layout — Interview Questions
  • JIT Compilation & Runtime Optimisations — Interview Questions
  • Garbage Collectors Deep Dive — Interview Questions
  • JVM Tuning, GC Logs & Memory Footprint — Interview Questions
  • Memory Leaks, OutOfMemoryErrors & Profiling Tools — Interview Questions
  • Modules, Agents & Advanced JVM APIs — Interview Questions

Collections & Concurrency at Scale

  • Collections Internals & Complexity — Interview Questions
  • Iterators, Comparators & Ordering Contracts — Interview Questions
  • Concurrent Collections, Queues & Lock-Free Structures — Interview Questions
  • Threads, Executors & ForkJoin Internals — Interview Questions
  • Locks, Atomics, CAS & Synchronizers — Interview Questions
  • Java Memory Model, volatile, Fences & ThreadLocal — Interview Questions
  • Deadlock, Livelock, Starvation & Concurrent Design — Interview Questions
  • CompletableFuture, Parallel Streams & Non-Blocking I/O — Interview Questions

Modern Java (8 to 21+)

  • Lambdas & Functional Interfaces Internals — Interview Questions
  • Streams & Collectors Deep Dive — Interview Questions
  • Optional & Interface Default/Static Methods — Interview Questions
  • Java 9–25 Features & Virtual Threads — Interview Questions

Design Patterns, SOLID & Clean Code

  • Design Pattern Trade-offs & Combinations — Interview Questions
  • SOLID, Clean Code & Anti-Patterns — Interview Questions

Spring & Spring Boot Internals

  • IoC, Dependency Injection & Bean Lifecycle Internals — Interview Questions
  • Spring AOP, Proxies & @Async Internals — Interview Questions
  • Spring Configuration, Auto-Configuration & Custom Starters — Interview Questions
  • Spring MVC & REST Internals, Exception Frameworks — Interview Questions
  • Spring Security Advanced Internals — Interview Questions
  • Spring WebFlux, Reactor & R2DBC — Interview Questions
  • Spring Cloud, Observability & Distributed Tracing — Interview Questions
  • Spring Boot 3, Native Images & Production Scenarios — Interview Questions

JPA, Hibernate & Databases at Scale

  • Spring Data JPA — Queries, Projections, Custom Repositories & Locking — Interview Questions
  • JPA Entity Mapping, Associations & Cascades — Interview Questions
  • JPQL vs Native Queries in Depth — Interview Questions
  • Hibernate Caching — First-Level, Second-Level & Query Cache — Interview Questions
  • Lazy vs Eager Loading, LazyInitializationException & N+1 — Interview Questions
  • JPA Transactions, Propagation, Isolation & Dirty Checking — Interview Questions
  • SQL vs NoSQL, Indexing & Query Tuning — Interview Questions
  • Database Scaling, Replication, Pooling & Consistency Models — Interview Questions
  • Redis, Search, Time-Series, CDC & Transactional Data Modelling — Interview Questions

Testing Strategy & API Design

  • Spring Boot Test Slices, Context & Test Strategy — Interview Questions
  • Testing Web, Persistence, Security, Async & Messaging in Spring Boot — Interview Questions
  • JUnit 5 & Mockito, Advanced — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 8+ Years (Senior & Lead)Design Patterns, SOLID & Clean Code
✓ FreeAdvanced· 12 min read

Design Pattern Trade-offs & Combinations — Interview Questions

Senior-level pattern judgement — factories with Supplier and lambdas, Builder and immutability, Prototype vs Builder, Decorator and Strategy for the Open/Closed Principle, how Facades create coupling, Proxy + Strategy caching, Comparator as Strategy, Command vs Strategy, LSP violations in Template Method, combining State and Strategy, asynchronous Commands, macro commands, and the design issues of mixing Strategy with Template Method.

Published September 25, 2026


How to use this lesson

At 8+ years, pattern questions aren't definitions (the 5–8 years tier covers those). They're about trade-offs, combinations, and failure modes. Use modern Java idioms (lambdas, records, sealed types), and tie every answer to a principle: OCP, LSP, or coupling and cohesion.

Q1. How can you implement the Factory pattern with Java 8 Suppliers or lambdas?

Short answer: Replace a switch-based factory, or one factory class per product, with a registry of Suppliers or constructor references. That's open for extension (register new types), and there's no class explosion.

public final class ExporterFactory {
    private final Map<Format, Supplier<Exporter>> registry = new EnumMap<>(Format.class);
    public ExporterFactory() {
        registry.put(Format.CSV,  CsvExporter::new);
        registry.put(Format.JSON, JsonExporter::new);
        registry.put(Format.PDF,  () -> new PdfExporter(PageSize.A4));      // lambdas can pass configuration
    }
    public Exporter create(Format f) {
        Supplier<Exporter> s = registry.get(f);
        if (s == null) throw new IllegalArgumentException("Unsupported format " + f);
        return s.get();
    }
}
// Parameterised creation: Function<Config, Exporter>, or a BiFunction for two arguments

Key points to cover:

  • In Spring, the "registry" is often an injected Map<String, Exporter> of beans, or ObjectProvider<T> for prototype-scoped products.

Learn it in depth → Factory & Abstract Factory

Q2. How does the Builder pattern improve immutability and readability?

Short answer:

  • Immutability: the mutable state lives in the builder. build() validates it, and passes a complete snapshot to a private constructor that assigns final fields (defensively copying collections). The product never exists in a half-initialised or mutable state, so it's safe to share across threads.
  • Readability: named, chained steps (.timeout(Duration.ofSeconds(5)).retries(3)) replace positional arguments (new Client(5, 3, true, null)). Optional parameters get defaults. Invariants across fields are checked in one place.
  • Evolution: adding an optional field doesn't break callers.

With records, a nested builder, or "wither" methods (withTimeout(...) returning a copy), gives the same immutability for simple cases.

Learn it in depth → Builder Pattern

Q3. Give a real-world case where Prototype is preferred over Builder.

Short answer: When objects are expensive to configure from scratch, but many near-identical copies are needed, Prototype wins. You clone a pre-configured template, then tweak only what differs:

  • document or report templates, loaded and parsed once from storage (layout, styles, sections), cloned per report, and filled with data;
  • game entities or simulation agents, spawned from a template with preloaded assets;
  • pricing plans or rule sets copied per tenant, then overridden;
  • test fixtures: a baseline order, copied and varied per test.

Builder is better when every object is assembled differently from parameters. Prototype is better when you copy an existing, richly configured instance, especially when the concrete class is only known at runtime (a registry of prototypes).

Q4. How does the Decorator pattern support the Open/Closed Principle? How does Strategy?

Short answer: Both let you add behaviour without modifying existing, tested code:

  • Decorator extends behaviour by wrapping: new features (caching, retries, metrics, encryption) are new classes implementing the same interface. The original class stays closed for modification, and the system is open for extension, by composing decorators at configuration time.
  • Strategy extends by substitution: the context depends on an interface. A new algorithm (a new payment method or pricing rule) is a new class or bean, and the context's code never changes. That removes the if/else or switch that would otherwise need editing (the typical OCP violation).

The difference: decorators stack around one implementation, while strategies replace each other.

Learn it in depth → Single Responsibility & Open/Closed

Q5. How can a Facade introduce tight coupling if it's used carelessly?

Short answer:

  • A god facade: one facade exposing everything the subsystem does becomes a central dependency for many clients. Every subsystem change ripples through it, and it changes for many unrelated reasons (an SRP violation).
  • Leaky facades: returning subsystem types (entities, SDK objects) couples clients to the internals, which defeats the facade.
  • Hidden workflows: business rules embedded in the facade make it the only place logic lives. Clients then can't reuse the subsystems independently, and the facade and subsystems become deployment-coupled.
  • Forcing all access through the facade blocks legitimate advanced use, and leads to more and more parameters and flags ("facade creep").

Mitigations: several role-specific facades, DTOs at the boundary, keeping domain logic in the domain services, and letting advanced clients bypass it.

Q6. How would you combine a Proxy with a Strategy for caching?

Short answer: A caching proxy implements the service interface, and intercepts calls. Which caching policy it uses is a pluggable Strategy (the eviction policy, the TTL rules, the key generation, or local vs distributed storage):

interface CachePolicy<K, V> { Optional<V> get(K key); void put(K key, V value); }   // strategy: LRU, TTL, Redis…

final class CachingPriceService implements PriceService {                         // proxy
    private final PriceService target;
    private final CachePolicy<String, Price> cache;
    CachingPriceService(PriceService target, CachePolicy<String, Price> cache) { this.target = target; this.cache = cache; }
    @Override public Price price(String sku) {
        return cache.get(sku).orElseGet(() -> { Price p = target.price(sku); cache.put(sku, p); return p; });
    }
}
PriceService service = new CachingPriceService(new RemotePriceService(), new TtlCachePolicy<>(Duration.ofMinutes(5)));

Key points to cover:

  • Spring's @Cacheable is exactly this: an AOP proxy, plus a pluggable CacheManager (Caffeine, Redis) as the strategy.

Q7. Give an example of how Java's Comparator uses the Strategy pattern.

Short answer: Collections.sort(list, comparator), List.sort, TreeMap(comparator) and stream.sorted(comparator) are contexts whose sorting algorithm stays fixed (TimSort, a red-black tree), while the comparison strategy is injected. You swap orderings (by name, by price descending, locale-aware) without changing the sorting code. Comparators compose (thenComparing, reversed, nullsFirst), which is strategy composition.

Comparator<Product> byPriceThenName = Comparator.comparing(Product::price).thenComparing(Product::name);
products.sort(byPriceThenName);                       // the same sort algorithm, a different strategy
products.sort(byPriceThenName.reversed());

Q8. Give a real-world case where Command is better than Strategy.

Short answer: When you need to treat requests as objects with a lifecycle: queue them, log them, undo them, retry them, or schedule them. For example:

  • An editor or design tool, where each user action (MoveShape, ChangeColor) is a command with execute() and undo(), stored on undo and redo stacks.
  • A job scheduler or workflow engine, where operations are serialised to a queue (SendInvoiceCommand), executed later by workers, retried with idempotency keys, and audited.
  • CQRS write models: PlaceOrderCommand objects validated, authorised and handled uniformly.

Strategy answers "how do I compute this" (an interchangeable algorithm, called synchronously). Command answers "what should be done, by whom, and when" (a request object with its data, receiver and history).

Q9. How can violating the Liskov Substitution Principle affect the Template Method pattern?

Short answer: The template method's base class assumes certain contracts from its hook methods (pre- and post-conditions, no side effects, no exceptions of certain kinds, call-order expectations). A subclass that violates them breaks the algorithm, even though it compiles. For example:

  • validate() hook overridden to throw UnsupportedOperationException, or to skip validation: the base workflow then persists invalid data;
  • a strengthened precondition: the subclass's parse() rejects inputs the base class guaranteed would be accepted;
  • side effects or state in hooks the base class calls more than once, or in an unexpected order;
  • overriding the template method itself, if it isn't final, reordering the steps.

Clients depending on AbstractImporter then can't substitute that subclass safely: an LSP violation. The mitigations:

  • make the template method final;
  • document the hook contracts;
  • keep hooks small and side-effect free;
  • prefer composition (strategies) over deep inheritance.

Q10. When would you combine the State and Strategy patterns?

Short answer: When an object's behaviour depends on its lifecycle state (State), and some step within a state has interchangeable algorithms (Strategy). For example, in an order:

  • State objects (Created, Paid, Shipped, Cancelled) decide which transitions are allowed, and what cancel() means in each state;
  • in the Paid state, the refund strategy varies by payment method (card void vs refund, UPI refund API, wallet credit), and it's injected as a Strategy;
  • or a pricing strategy that varies by customer tier, used within the Checkout state.

The key distinction: states replace themselves (transitions are internal), while strategies are chosen by the client or configuration, and don't know about each other. Combining them keeps each concern focused.

Learn it in depth → State Pattern

Q11. How would you implement asynchronous Command execution?

Short answer: Separate invocation from execution, using a queue:

  1. Commands are serialisable data (records), plus a handler resolved by type.
  2. The invoker enqueues them: an in-process BlockingQueue with an executor, or a durable broker (Kafka, RabbitMQ, SQS) for reliability.
  3. Workers dequeue and run the handlers. They need idempotency keys (safe retries), retries with backoff, dead-letter queues, and timeouts.
  4. Results and status: return a CompletableFuture in-process, or a job ID that the client polls (202 Accepted + /jobs/{id}), or a webhook or event on completion.
  5. Add ordering per aggregate (partition key) where needed.
record SendInvoice(UUID idempotencyKey, UUID orderId) implements Command {}

class AsyncCommandBus {
    private final ExecutorService workers = Executors.newVirtualThreadPerTaskExecutor();
    private final Map<Class<?>, CommandHandler<?>> handlers;
    AsyncCommandBus(Map<Class<?>, CommandHandler<?>> handlers) { this.handlers = handlers; }

    @SuppressWarnings("unchecked")
    <C extends Command> CompletableFuture<Void> dispatch(C cmd) {
        var handler = (CommandHandler<C>) handlers.get(cmd.getClass());
        return CompletableFuture.runAsync(() -> handler.handle(cmd), workers);
    }
}

Q12. How does the Command pattern help implement macro operations?

Short answer: A macro command is a Composite of commands: it holds a list of commands, execute() runs them in order, and undo() undoes them in reverse order. A macro is itself a Command, so it can be queued, logged, undone as one unit, or nested in other macros. To make it atomic, track the executed sub-commands. If one fails midway, compensate the ones already done (the same idea as a saga).

final class MacroCommand implements Command {
    private final List<Command> steps; private final Deque<Command> done = new ArrayDeque<>();
    MacroCommand(List<Command> steps) { this.steps = List.copyOf(steps); }
    public void execute() {
        try { for (Command c : steps) { c.execute(); done.push(c); } }
        catch (RuntimeException e) { undo(); throw e; }                 // all-or-nothing
    }
    public void undo() { while (!done.isEmpty()) done.pop().undo(); }
}

It's used for "Apply formatting" in editors, batch admin operations, and recorded user workflows.

Q13. What design issues arise when combining Strategy and Template Method?

Short answer: They can coexist (a template method whose steps delegate to injected strategies), but there are pitfalls:

  • Two extension mechanisms for the same variation: developers don't know whether to subclass or inject, so you get duplicated or conflicting customisations.
  • Inheritance plus composition complexity: behaviour is spread across a base class, subclasses and strategies, which makes it hard to trace and test.
  • Hidden coupling: a subclass's hook may assume a particular strategy (or vice versa), creating implicit dependencies.
  • Fragile base class: changes in the template's step order affect every subclass and strategy combination.
  • Testing explosion: subclasses × strategies.

The guidance: pick one variation mechanism per dimension:

  • a final template for the invariant workflow, with strategies for the variable steps (composition), and no subclass hooks;
  • or refactor to a pipeline of strategies (like Spring's JdbcTemplate callbacks).

Document which points are meant to vary.

Q14. How does the Strategy pattern promote the Open/Closed Principle? (The DXA framing)

Short answer: The context is closed: it depends only on the Strategy interface, and never needs editing. The system is open: a new behaviour is a new implementation, registered by configuration or dependency injection. Removing switch/if-else on type codes means new cases don't touch old code or old tests, which reduces regression risk. With sealed interfaces, you can deliberately choose the opposite trade-off: closed types, with exhaustive switches, when the set of variants should not be extensible.

Follow-up questions this topic invites — and their answers

Q: Decorator vs Proxy vs Adapter, in one line each? A: A decorator adds behaviour through the same interface. A proxy controls access through the same interface (lazy, remote, security, caching). An adapter converts one interface into another.

Q: Is dependency injection a pattern that replaces Factory? A: Partly. The DI container is a giant, configurable factory, and assembles object graphs. You still write explicit factories for runtime-parameterised creation (objects created per request, from user input), often exposed as beans or ObjectProviders.

Q: What's the risk of overusing patterns? A: Over-engineering: indirection without variability, and more classes for readers to navigate (a YAGNI violation). Introduce a pattern when you have at least two real variants, or a concrete extension need, and refactor into it when the need appears.

Q: How do sealed interfaces change the Visitor pattern? A: With sealed hierarchies and pattern-matching switch, you can write exhaustive, type-safe operations over a closed set of types without Visitor's double-dispatch boilerplate. The compiler flags any missing case when a type is added.

Previous

Java 9–25 Features & Virtual Threads — Interview Questions

Next

SOLID, Clean Code & Anti-Patterns — Interview Questions

AI Tutor

Lesson: Design Pattern Trade-offs & Combinations — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.