🎨

Top 50 Design Patterns (Gang of Four) Interview Questions & Answers (2026)

50 Questions 20 Beginner 20 Intermediate 10 Advanced

About Design Patterns (Gang of Four)

Top 50 Design Patterns interview questions covering creational, structural, and behavioral patterns from the Gang of Four book. Companies hiring for Design Patterns (Gang of Four) roles test this knowledge at every stage — from first-round screens that check the basics to final-round conversations about real-world trade-offs.

What to Expect in a Design Patterns (Gang of Four) Interview

Expect a mix of conceptual and practical Design Patterns (Gang of Four) questions: clear definitions and core-concept checks for junior roles, hands-on scenario questions for mid-level roles, and architecture or trade-off discussions for senior roles. Interviewers usually move from foundational topics toward the kind of problems you'd actually face on the job.

How to Use This Guide

Work through the Design Patterns (Gang of Four) questions in order — Beginner, then Intermediate, then Advanced — so each concept builds on the last. Every question has its own page; bookmark the ones that trip you up and revisit them the day before your interview.

Curated by Tech Baithak Editorial Team  ·  Last updated: June 2026

Beginner 20 questions

Core concepts every Design Patterns (Gang of Four) developer must know.

01

What is the Singleton design pattern?

The Singleton pattern ensures that a class has only one instance throughout the application's lifetime and provides a global access point to that instance. It is implemented by making the constructor private, storing the instance in a static variable, and exposing a static getInstance() method. Common real-world uses include a database connection pool, a logging service, or a configuration manager where having multiple instances would waste resources or cause inconsistent state. Care must be taken in multithreaded environments to prevent two threads from creating two instances simultaneously.

Open this question on its own page
02

What is the Factory Method design pattern?

The Factory Method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. The creator class declares an abstract factory method, and each concrete subclass overrides it to produce a product appropriate for itself. For example, a LogisticsApp might declare a createTransport() factory method, with RoadLogistics returning a Truck and SeaLogistics returning a Ship. This decouples product creation from the code that uses the product, making it easy to add new product types by adding new subclasses.

Open this question on its own page
03

What is the Abstract Factory design pattern?

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. Think of it as a factory of factories. For example, a UI toolkit might have a GUIFactory interface with methods createButton() and createCheckbox(). Concrete implementations like WindowsFactory and MacFactory produce Windows-style or Mac-style widgets respectively. Client code only depends on the abstract interface, so you can switch the entire family of products by swapping the factory without touching any client code.

Open this question on its own page
04

What is the Builder design pattern?

The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to produce different representations. Instead of a constructor with many parameters (a "telescoping constructor" anti-pattern), a builder object accumulates configuration through method calls and produces the final object at the end. For example, building an HTTP request: new RequestBuilder().url("...").method("POST").body(json).timeout(30).build(). This is very common in Java (e.g., StringBuilder, Retrofit's builder) and Kotlin's DSL builders.

Open this question on its own page
05

What is the Prototype design pattern?

The Prototype pattern creates new objects by copying (cloning) an existing object, called the prototype. Instead of instantiating a new object from scratch, you call a clone() method on the prototype. This is useful when object creation is expensive (e.g., loading from a database or performing heavy computation) and you need many similar objects. For example, a game that spawns many enemies of the same type can clone a prototype enemy instead of re-loading all assets for each spawn. Java's Cloneable interface and Python's copy.deepcopy() are standard implementations.

Open this question on its own page
06

What is the Adapter design pattern?

The Adapter pattern converts the interface of a class into another interface that clients expect, allowing classes to work together that otherwise could not because of incompatible interfaces. It acts like a real-world power plug adapter. For example, if your app expects a Logger interface but you want to use a third-party library with a FileWriter class, you create a FileWriterAdapter that implements Logger and delegates calls to FileWriter. This lets you integrate legacy or third-party code without modifying either the client or the existing class.

Open this question on its own page
07

What is the Bridge design pattern?

The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently. Without Bridge, combining two dimensions of variation (e.g., shape types and rendering APIs) leads to an exponential explosion of subclasses. Bridge solves this by making the abstraction hold a reference (bridge) to an implementation object. For example, a Shape abstraction can hold a Renderer implementation, so you can have Circle and Square work with either SVGRenderer or CanvasRenderer without four separate classes. The key intent is to split one large class into two independent hierarchies.

Open this question on its own page
08

What is the Composite design pattern?

The Composite pattern composes objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects and compositions uniformly. Both leaf objects and container objects implement the same Component interface. For example, a file system has File (leaf) and Folder (composite) objects; calling getSize() on a Folder recursively sums the sizes of all its children. This pattern is ideal for UI widget hierarchies, organizational charts, and any domain where objects can be nested inside other objects of the same type.

Open this question on its own page
09

What is the Decorator design pattern?

The Decorator pattern attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality. Decorators wrap the original object and implement the same interface, forwarding calls while adding behavior before or after. Java's I/O streams are the classic example: new BufferedReader(new FileReader("file.txt")) wraps a file reader with buffering. You can stack multiple decorators: new EncryptedStream(new CompressedStream(new FileStream(...))). This avoids the class explosion that would result from inheriting every combination of features.

Open this question on its own page
10

What is the Facade design pattern?

The Facade pattern provides a simplified interface to a complex subsystem of classes. It does not hide the subsystem — it just gives clients a clean, high-level entry point. For example, a home theater system has many components (projector, amplifier, DVD player, lights). A HomeTheaterFacade exposes simple methods like watchMovie() and endMovie() that internally coordinate all the components. Facades improve code readability and reduce coupling between the client and the subsystem. Frameworks like Laravel and Spring are essentially large facades over complex infrastructure.

Open this question on its own page
11

What is the Flyweight design pattern?

The Flyweight pattern reduces memory usage by sharing as much data as possible with similar objects. It separates an object's state into intrinsic (shared, immutable, stored in the flyweight) and extrinsic (unique per context, passed in at runtime) state. A classic example is a text editor that renders millions of characters — instead of creating a separate object for every 'e' on the page, one flyweight object represents the letter 'e' and its position (extrinsic) is passed in when rendering. Java's String.intern() and integer caching (-128 to 127) use this principle.

Open this question on its own page
12

What is the Proxy design pattern?

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. The proxy implements the same interface as the real object, so clients cannot tell the difference. Common types include: Virtual Proxy (lazy initialization — creates the expensive object only when first needed), Protection Proxy (access control — checks permissions before forwarding), and Remote Proxy (communicates with an object in a different address space over a network). Java's RMI stubs and Spring's @Transactional AOP proxies are well-known examples.

Open this question on its own page
13

What is the Chain of Responsibility design pattern?

The Chain of Responsibility pattern passes a request along a chain of handlers. Each handler decides either to process the request or to pass it to the next handler in the chain. This decouples the sender of a request from its receivers. A practical example is HTTP middleware in web frameworks — a request passes through authentication middleware, then logging middleware, then rate-limiting middleware before reaching the controller. Another example is a customer support system where a request escalates from a chatbot to a tier-1 agent to a specialist. The pattern allows you to assemble chains dynamically at runtime.

Open this question on its own page
14

What is the Command design pattern?

The Command pattern encapsulates a request as an object, allowing you to parameterize clients with different requests, queue them, log them, and support undoable operations. Each command object implements an execute() method. For example, a text editor's "Bold" action becomes a BoldCommand object. This enables: storing commands in a history stack for undo/redo, serializing commands to a queue for scheduled execution, and implementing macro recording by grouping multiple commands. GUI buttons that execute different actions depending on context are a textbook use case.

Open this question on its own page
15

What is the Iterator design pattern?

The Iterator pattern provides a standard way to sequentially access elements of a collection without exposing its underlying representation (array, tree, linked list, etc.). It defines a hasNext() and next() interface. This allows the same client code to traverse different collection types uniformly. Java's Iterator interface and Python's __iter__/__next__ protocol are the most prominent implementations. The pattern also allows multiple independent iterators on the same collection simultaneously, each maintaining its own traversal state without interfering with each other.

Open this question on its own page
16

What is the Mediator design pattern?

The Mediator pattern reduces chaotic dependencies between objects by forcing them to communicate through a central mediator object instead of directly with each other. This turns a many-to-many web of dependencies into a hub-and-spoke model. An air traffic control tower is the classic analogy: planes don't communicate with each other; they all talk to the tower (mediator). In software, a chat room mediates message passing between users, and a UI dialog can act as a mediator — when a checkbox is clicked, instead of the checkbox knowing about the text field and the button, it notifies the dialog, which decides what to update.

Open this question on its own page
17

What is the Memento design pattern?

The Memento pattern captures and externalizes an object's internal state so it can be restored later, without violating encapsulation. Three roles are involved: the Originator (the object whose state is saved), the Memento (a snapshot of the state), and the Caretaker (stores and manages mementos but never inspects their contents). Text editors implementing undo/redo and database transactions (savepoints) are prime examples. The critical constraint is that only the originator can read the memento's contents, preserving its encapsulation from the caretaker.

Open this question on its own page
18

What is the Observer design pattern?

The Observer pattern defines a one-to-many dependency between objects so that when one object (the Subject or Publisher) changes state, all its dependents (Observers or Subscribers) are notified and updated automatically. This is the foundation of event-driven systems. Examples include: a spreadsheet where formulas (observers) automatically recalculate when a cell (subject) value changes, and a news agency that notifies all subscribed news feeds when a new story is published. Java's Observer/Observable (deprecated), JavaScript's EventEmitter, and reactive frameworks all implement this pattern.

Open this question on its own page
19

What is the State design pattern?

The State pattern allows an object to alter its behavior when its internal state changes, making it appear to change its class. Instead of large conditional (if/switch) blocks based on a state variable, the context object delegates behavior to a state object that represents the current state. When the state changes, the context swaps its state object. A vending machine is a perfect example — it behaves very differently when NoMoneyState vs HasMoneyState vs OutOfStockState. This makes adding new states easy without modifying existing state classes, following the Open/Closed Principle.

Open this question on its own page
20

What is the Strategy design pattern?

The Strategy pattern defines a family of algorithms, encapsulates each one in a separate class, and makes them interchangeable. The context object holds a reference to a strategy interface and delegates the algorithm to whichever concrete strategy is currently set. For example, a payment processor can use a CreditCardStrategy, PayPalStrategy, or CryptoStrategy interchangeably — the context (checkout) doesn't need to know which one is active. This eliminates conditional logic for choosing algorithms and makes it trivial to add new strategies. Java's Comparator is a well-known implementation of this pattern.

Open this question on its own page
Intermediate 20 questions

Practical knowledge for developers with hands-on experience.

01

What is the Template Method design pattern?

The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. The base class method calls abstract or overridable "hook" methods in a fixed order. Subclasses fill in the specific steps without changing the overall algorithm structure. For example, a data mining framework might define: openFile()extractData()parseData()analyzeData()closeFile(). The base class implements the invariant steps; each concrete miner implements extractData() and parseData() for its specific format (CSV, XML, PDF). This is the foundation of the Hollywood Principle: "Don't call us, we'll call you."

Open this question on its own page
02

What is the Visitor design pattern?

The Visitor pattern lets you add new operations to a class hierarchy without modifying those classes, by separating the algorithm from the object structure. You define a Visitor interface with a visit() method for each element type. Each element implements an accept(Visitor v) method that calls v.visit(this) — this "double dispatch" technique ensures the correct visitor method is called based on both the element type and the visitor type. Use it when you have a stable set of element types but need to frequently add new operations. XML/AST processors and compiler optimization passes are classic examples.

Open this question on its own page
03

What is the difference between Factory Method and Abstract Factory?

The Factory Method uses inheritance — a single factory method in a base class is overridden by subclasses to produce one type of product. It handles one product at a time and is about letting subclasses decide which class to instantiate. Abstract Factory uses composition — a factory interface produces families of related products through multiple factory methods. It coordinates the creation of multiple related objects that are designed to work together. Use Factory Method when you need one product and want subclasses to control which type; use Abstract Factory when you need to enforce that an entire family of products is used together (e.g., all Windows widgets or all Mac widgets, never mixed).

Open this question on its own page
04

When should you NOT use design patterns?

Design patterns should not be applied mechanically to every problem. Overusing them leads to over-engineering — adding unnecessary layers of abstraction, indirection, and complexity for problems that a simple function or class would solve more clearly. Avoid patterns when: the codebase is small and unlikely to grow in the anticipated direction, the pattern adds multiple classes where one would do, you are applying a pattern preemptively "just in case," or the team is unfamiliar with the pattern and will struggle to maintain it. The costs of patterns (added classes, indirection, learning curve) must be outweighed by the benefit (flexibility, reuse). As a rule: reach for simple solutions first; introduce a pattern only when a concrete need emerges.

Open this question on its own page
05

How are the Gang of Four patterns categorized?

The 23 GoF patterns are divided into three categories based on their purpose. Creational patterns (5) deal with object creation: Singleton, Factory Method, Abstract Factory, Builder, Prototype. Structural patterns (7) deal with composing classes and objects: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy. Behavioral patterns (11) deal with communication and responsibility between objects: Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor. Creational patterns abstract the instantiation process; structural patterns assemble objects into larger structures; behavioral patterns define how objects cooperate to carry out complex tasks.

Open this question on its own page
06

How are GoF patterns used in real-world frameworks?

GoF patterns appear throughout major frameworks. In Spring (Java): Singleton (all beans are singletons by default), Factory Method (BeanFactory), Proxy (@Transactional, AOP), Template Method (JdbcTemplate, RestTemplate), Observer (ApplicationEvents). In Laravel (PHP): Facade pattern is prominently used for the service container, Factory for model factories, Observer for Eloquent model events, Decorator in middleware pipeline. In React: Observer (state and props reactivity), Composite (component tree). In Java Collections: Iterator (all collection traversal), Flyweight (String pool), Decorator (I/O streams). Recognizing patterns in frameworks helps you understand and use them more effectively.

Open this question on its own page
07

How can you combine the Decorator and Strategy patterns?

Decorator and Strategy are complementary: Decorator adds behavior by wrapping objects, while Strategy swaps algorithms inside an object. A common combination is a configurable pipeline. Imagine a data processor where you can decorate a base processor with logging, caching, or retry behavior (Decorator), and within each step the specific algorithm is pluggable (Strategy). For example, an HTTP client decorator adds retry logic, while the retry Strategy determines whether to use exponential backoff or fixed-interval retry. This combination produces highly flexible and testable code. The key distinction: Decorator modifies what an object does structurally; Strategy modifies how it does it algorithmically.

Open this question on its own page
08

What is the difference between Decorator and Composite?

Both Decorator and Composite use recursive composition and share the same component interface, which is why they are often confused. The key differences: Decorator always wraps a single child and adds behavior to it — its purpose is feature augmentation. Composite wraps multiple children and aggregates them — its purpose is representing tree structures. In a Decorator, the relationship is 1-to-1; in a Composite, it is 1-to-many. You can often combine them: a file system uses Composite for the folder/file hierarchy, and then you can decorate specific files with an encryption decorator. Decorator's chain is linear; Composite's structure is a tree.

Open this question on its own page
09

What is the difference between Observer and Mediator?

Both patterns deal with object communication, but they differ in structure and intent. In Observer, the subject knows its observers (holds a list of them) and directly notifies them; observers can subscribe/unsubscribe dynamically. This creates a direct dependency from subject to its observers. In Mediator, no object knows about any other object — they all know only the mediator, which orchestrates the interaction. Observer is best for simple event broadcasting where a change in one object needs to propagate to many others. Mediator is best when many objects interact in complex ways and you want to centralize that interaction logic to avoid a tangled web of cross-references.

Open this question on its own page
10

What is the difference between Strategy and Template Method?

Both Strategy and Template Method encapsulate algorithms, but they do so differently. Template Method uses inheritance: the algorithm skeleton is fixed in the base class and subclasses override specific steps — the hook calls are built into the class hierarchy and cannot be changed at runtime. Strategy uses composition: the entire algorithm is encapsulated in a strategy object that is injected into the context and can be swapped at runtime. Template Method is simpler when you have one algorithm with a few variable steps. Strategy is more flexible, more testable, and preferred by modern design (favoring composition over inheritance). In practice, Strategy + dependency injection has largely superseded Template Method.

Open this question on its own page
11

What are the different types of Proxy?

The GoF Proxy pattern has several common variants. A Virtual Proxy delays the creation of an expensive object until it is actually needed (lazy initialization) — a high-resolution image thumbnail is a virtual proxy for the full image. A Protection Proxy controls access to the real object, checking permissions before forwarding calls — useful for role-based access control. A Remote Proxy provides a local representative for an object in a different address space or machine — RMI stubs in Java are remote proxies. A Caching Proxy stores results of expensive operations and returns cached results for repeated requests. A Logging Proxy records method calls and arguments for auditing.

Open this question on its own page
12

How does the Command pattern support undo/redo?

The Command pattern enables undo/redo by storing executed command objects in a history stack. Each command implements both execute() and undo() methods. To undo, pop the last command from the history stack and call its undo() method, which reverses the operation. To redo, push the command back and call execute() again. For example, a MoveShapeCommand stores the shape, the old position, and the new position. execute() moves the shape to the new position; undo() moves it back to the old position. Text editors, graphic tools, and spreadsheets all implement undo/redo this way, often maintaining separate undo and redo stacks.

Open this question on its own page
13

What is the difference between Bridge and Adapter?

Both Bridge and Adapter involve wrapping one interface with another, but their intent and timing differ fundamentally. Adapter is applied after the fact to make two incompatible existing interfaces work together — it is a patch. Bridge is designed upfront to prevent a class hierarchy from growing exponentially when two independent dimensions of variation exist. Adapter changes the interface of an existing class without changing its behavior. Bridge defines both the abstraction and implementation interfaces from the start and keeps them separate so they can vary independently. If you're integrating a legacy class with a new interface, use Adapter. If you're designing a new class hierarchy with multiple varying dimensions, use Bridge.

Open this question on its own page
14

How does the Facade pattern differ from Mediator?

Both Facade and Mediator hide complexity behind a simpler interface, but their roles differ. A Facade provides a simplified one-way interface to a complex subsystem — the subsystem classes are unaware of the facade's existence, and the facade does not coordinate between subsystem classes; it simply delegates. A Mediator actively coordinates two-way communication between multiple colleague objects — all colleagues know the mediator and send messages through it. The facade is a traffic simplifier for client code; the mediator is a traffic controller that orchestrates peer-to-peer communication. In a chat app, a facade wraps the complex networking layer; a mediator manages the communication between users.

Open this question on its own page
15

What are the internals of the Iterator pattern?

The Iterator pattern defines two key interfaces: Iterator (with hasNext(), next(), and optionally remove()) and Iterable/Aggregate (with createIterator()). The concrete iterator holds a reference to the collection and a current position cursor. When next() is called, it returns the current element and advances the cursor. Importantly, the iterator is stateful and independent from the collection — you can have two iterators on the same collection traversing at different speeds simultaneously. In Java, the for-each loop desugars to an iterator call. For tree collections, you can have different iterator implementations for pre-order, in-order, and post-order traversal without changing the tree class.

Open this question on its own page
16

What are practical use cases for the Memento pattern?

The Memento pattern is used whenever you need to save and restore the state of an object without exposing its implementation details. Key use cases: Undo/redo in text editors, graphic tools, and IDEs (each state before an edit is a memento). Database savepoints/transactions — rolling back to a known good state. Game save files — capturing the full game state at a checkpoint. Wizard forms — storing the state of each step so the user can navigate back. Configuration snapshots — allowing users to revert to a previous configuration. The pattern is appropriate when an object's state is complex and encapsulation must not be broken to save it.

Open this question on its own page
17

What is the difference between Chain of Responsibility and Decorator?

Both Chain of Responsibility and Decorator pass a request along a linked chain of objects, but their intent and behavior differ. In Decorator, every wrapper in the chain always processes the request and forwards it — the chain runs to completion, and behavior is accumulated at each step. In Chain of Responsibility, each handler decides independently whether to handle the request or pass it on — once a handler processes the request, it may stop propagation. Decorator is used to add cumulative features; Chain of Responsibility is used for conditional processing where the appropriate handler is not known in advance. HTTP middleware pipelines blend both: they always pass through (Decorator-like) but can short-circuit on error (Chain-like).

Open this question on its own page
18

How are GoF patterns used in Java and Python?

In Java, GoF patterns are deeply embedded in the standard library and ecosystem. Singleton is used for Runtime.getRuntime(); Factory Method in Calendar.getInstance(); Abstract Factory in the DocumentBuilderFactory (XML API); Iterator in java.util.Iterator; Observer in java.util.EventListener; Decorator in java.io streams; Flyweight in Integer.valueOf() caching. In Python, the language's dynamic nature means many patterns are simpler or built-in: Iterator via __iter__/__next__, Observer via signals (Django) or callbacks, Decorator via Python's native @decorator syntax (though not exactly GoF Decorator), Singleton via module-level instances. Python's duck typing often replaces the need for explicit interface definitions that Java requires for patterns.

Open this question on its own page
19

What is the Interpreter design pattern?

The Interpreter pattern defines a grammar for a language and provides an interpreter to process sentences in that language. Each grammar rule is represented as a class, and parsing a sentence means building a composite expression tree and calling interpret() on it. Classic examples include: regular expression engines, SQL query parsers, mathematical expression evaluators, and template engines. While conceptually important, it is rarely implemented from scratch today because parser-generator tools (ANTLR, Lark, PEG) are far more practical for real languages. The Interpreter pattern is most appropriate for simple, domain-specific languages with a small grammar.

Open this question on its own page
20

How does the Observer pattern relate to event-driven programming?

The Observer pattern is the conceptual foundation of all event-driven programming. Every event system — DOM events in browsers, Node.js EventEmitter, Java's Swing listeners, .NET's delegates and events, Android's setOnClickListener — is an implementation of Observer. Modern reactive frameworks like RxJava, RxJS, and Kotlin Coroutines Flows generalize Observer into a full asynchronous pipeline with operators (map, filter, debounce). The key principle is the same: a producer (subject/publisher) broadcasts events, and consumers (observers/subscribers) react. The shift from synchronous Observer to asynchronous Reactive Streams adds back-pressure handling, error management, and composable operators on top of the core pattern.

Open this question on its own page
Advanced 10 questions

Deep expertise questions for senior and lead roles.

01

How do you implement a thread-safe Singleton using double-checked locking?

The naive Singleton with a null check is not thread-safe — two threads can simultaneously see the instance as null and both create a new object. Double-checked locking solves this with minimal synchronization overhead. In Java: declare the instance field as volatile (critical — prevents CPU instruction reordering that could return a partially-constructed object), then check for null outside the synchronized block (fast path), enter the synchronized block, check again inside (slow path), and create if still null. The volatile keyword ensures that once the instance is written, all threads see the fully initialized object. An even simpler thread-safe alternative in Java is the Initialization-on-demand holder idiom: a static inner class holds the instance and is loaded lazily by the JVM class loader, which guarantees thread safety without any synchronization code.

Open this question on its own page
02

How do the SOLID principles relate to GoF patterns?

GoF patterns and SOLID principles are deeply intertwined — patterns are often concrete implementations of SOLID principles. SRP (Single Responsibility): Command encapsulates one operation; Facade groups related operations behind one interface. OCP (Open/Closed): Strategy, State, and Decorator allow behavior extension without modifying existing classes — adding a new strategy or decorator doesn't touch existing code. LSP (Liskov Substitution): all GoF patterns that use polymorphism rely on this — a concrete strategy must be substitutable for the abstract strategy. ISP (Interface Segregation): patterns like Iterator and Observer define lean, focused interfaces. DIP (Dependency Inversion): Factory Method, Abstract Factory, and Builder all invert the dependency on concrete classes — clients depend on abstractions, not on concrete implementations. Patterns are the "how"; SOLID principles are the "why."

Open this question on its own page
03

What is the difference between GoF anti-patterns and the patterns themselves?

An anti-pattern is a common solution to a recurring problem that seems reasonable but actually makes things worse. Several GoF patterns become anti-patterns when misused. Singleton is widely considered an anti-pattern when overused: it introduces global mutable state, makes testing extremely difficult (hard to mock), and creates hidden coupling. The solution is dependency injection instead. Service Locator (not GoF but related) hides dependencies and makes code hard to test. God Object is what happens when Facade is taken too far — one class does everything. Premature abstraction is applying patterns (especially Abstract Factory or Strategy) before there is evidence of needing extensibility, making simple code needlessly complex. The lesson: patterns are tools, not goals — always weigh the added complexity against the actual benefit.

Open this question on its own page
04

How are GoF patterns used in modern frameworks like Spring and Django?

Modern frameworks are essentially codified collections of GoF patterns applied at scale. In Spring: the IoC container is an Abstract Factory that creates and wires beans; every singleton bean is a Singleton; @Transactional uses a Proxy (AOP) to wrap methods with transaction management; JdbcTemplate uses Template Method, with the SQL and result extraction as the variable steps; ApplicationEvents implement Observer. In Django: the ORM's QuerySet uses a lazy Proxy; Django signals implement Observer; class-based views use Template Method; the AUTH_USER_MODEL setting is an Abstract Factory substitution. In Laravel: facades are essentially a Proxy + Singleton combination, the service container is an Abstract Factory, Eloquent model events use Observer, and middleware implements Chain of Responsibility. Understanding these mappings lets you reason about framework behavior at a deeper level.

Open this question on its own page
05

What criteria should guide pattern selection?

Pattern selection should be driven by the specific problem you face, not by a desire to use patterns. Key criteria: (1) Variability — what aspect of the design is likely to change? If the algorithm varies, use Strategy. If the object's state varies, use State. If the family of products varies, use Abstract Factory. (2) Coupling — do you need to decouple object creation (Creational), structure (Structural), or communication (Behavioral)? (3) Extension direction — do you need to add new operations without changing objects (Visitor) or add new objects without changing operations (Strategy)? (4) Team familiarity — a well-known pattern communicates intent immediately to any developer who recognizes it; an obscure pattern adds cognitive load. (5) Testability — patterns that rely on composition (Strategy, Observer) are generally more testable than those that rely on inheritance (Template Method) or global state (Singleton).

Open this question on its own page
06

How do GoF patterns apply in functional programming?

Functional programming languages make many GoF patterns either trivial or unnecessary by using first-class functions and higher-order functions. Strategy becomes passing a function as an argument — no interface or class hierarchy needed. Command becomes a closure that captures context. Template Method becomes a higher-order function that accepts functions for the variable steps. Observer becomes subscribing a callback function or a reactive stream operator. Iterator is built into functional languages as lazy sequences or generators. Decorator maps to function composition — f(g(x)). Patterns like Singleton are avoided by passing dependencies explicitly (pure functions). Factory Method becomes a function that returns a value. The patterns that survive in FP tend to be higher-level architectural ones (Pipeline, Monad as a pattern) rather than the OOP-specific ones the GoF book addresses.

Open this question on its own page
07

What is the difference between dependency injection and the Service Locator pattern?

Both Dependency Injection (DI) and Service Locator address the same problem: how to provide an object with its dependencies without it constructing them directly. Service Locator provides a global registry that objects actively query — locator.get(Logger.class). This is considered an anti-pattern because: dependencies are hidden (not visible in constructors or method signatures), making the class impossible to test in isolation without the locator being configured, and tightly coupling the class to the locator infrastructure. In Dependency Injection, dependencies are pushed into the object from outside (via constructor, setter, or interface injection) — they are explicit and visible. This makes the class's requirements transparent, promotes testability (easy to inject mocks), and decouples the class from the DI framework entirely. Modern frameworks (Spring, Angular, Laravel's container) implement DI; Service Locator is the inferior predecessor.

Open this question on its own page
08

How does the Observer pattern scale in event-driven and distributed systems?

The classic GoF Observer has limitations at scale: the subject holds direct references to observers, making it synchronous, in-process, and tightly coupled. In event-driven architectures, this evolves into a publish-subscribe (Pub/Sub) model with a message broker (Kafka, RabbitMQ, Redis Pub/Sub) as the intermediary. Publishers emit events to a topic; subscribers consume from topics independently. This decouples producers and consumers completely — they don't know about each other, can run in separate processes, and can scale independently. Additional capabilities emerge: durable subscriptions (events are stored and can be replayed), fan-out (one event to thousands of consumers), and back-pressure. In microservices, this becomes the Event-Driven Architecture (EDA) pattern where services communicate exclusively through domain events, enabling loose coupling and eventual consistency across services.

Open this question on its own page
09

How can you use the Composite and Visitor patterns together?

The Composite + Visitor combination is one of the most powerful pattern pairings in software design. Composite creates a tree structure (AST, file system, UI widget tree, document model) where both leaves and composites share a common interface. The limitation of Composite alone is that adding a new operation (like "calculate size" or "render to HTML") requires modifying every node class. Visitor solves this: each node's accept(Visitor v) calls the appropriate v.visitLeaf() or v.visitComposite() method via double dispatch, and the Visitor class contains all the logic for one operation. Adding a new operation means adding a new Visitor class — no existing node classes are touched. Compilers use this combination extensively: an AST (Composite) is traversed by type-checking, optimization, and code-generation visitors, each a separate class with no changes to the AST node classes.

Open this question on its own page
10

How do GoF patterns apply to distributed systems design?

GoF patterns provide building blocks for distributed systems patterns. Proxy directly maps to network service proxies — gRPC stubs, API gateways, and sidecars (Envoy) are remote proxies that handle serialization, retries, and timeouts transparently. Observer scales to distributed Pub/Sub (Kafka, SNS) for event-driven microservices. Command is the foundation of Command Query Responsibility Segregation (CQRS) — requests are encapsulated as command objects, enabling audit logs, event sourcing, and asynchronous processing. Facade maps to the API Gateway pattern — a single entry point that simplifies access to multiple downstream services. Chain of Responsibility maps to middleware pipelines in service meshes and API gateways (authentication, rate limiting, logging). Strategy is used for pluggable retry, circuit-breaker, and load-balancing strategies in resilience libraries like Hystrix and Resilience4j. The core insight is that network boundaries, latency, and partial failures add new dimensions that GoF patterns must address through asynchrony and fault tolerance.

Open this question on its own page
Back to All Topics 50 questions total