Creational Patterns
Creational patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, and represented. Rather than hard-coding the specific classes that get instantiated, these patterns encapsulate knowledge about which concrete classes the system uses and hide how instances are created and put together.
As systems evolve to depend more on object composition than class inheritance, these patterns become increasingly important. They give you flexibility in what gets created, who creates it, how it's created, and when.
Two recurring themes run through all five: they encapsulate knowledge about which concrete classes the system uses, and they hide how instances are created and assembled.
Key Themes
- Hide concrete classes from clients
- Defer instantiation to subclasses or objects
- Program to an interface, not an implementation
- Support "open/closed" object creation
- Enable configuration at compile- or run-time
When a system must be independent of how its products are created, or when it must work with one of several families of products, Abstract Factory defines an interface for creating each kind of product. Concrete factories implement this interface for specific product families. Clients create objects solely through the factory interface and never know the concrete classes they're using.
Classic example: a UI toolkit supporting multiple look-and-feel standards (Motif, Windows, macOS). A WidgetFactory interface declares createScrollBar(), createButton(), etc. MotifWidgetFactory and WindowsWidgetFactory implement these to return the right widget style. Swapping the factory switches the entire look-and-feel at once.
- Cross-platform GUI toolkits (Swing Look & Feel, Qt styles)
- Database abstraction layers that must support multiple DB engines (MySQL, PostgreSQL, SQLite)
- Game engines that swap rendering backends (OpenGL, DirectX, Vulkan) without touching game logic
- Cloud provider SDKs abstracting compute, storage, and networking across AWS, Azure, GCP
- Theme systems in design frameworks (dark/light mode, brand families)
When object construction is complex and requires multiple steps, and when you need the same construction process to yield different results, Builder separates the construction algorithm (the Director) from the parts being assembled (the Builder). The client delegates construction to the Director, which uses the Builder interface step-by-step.
Classic example: an RTF document reader that must convert RTF to different formats (plain ASCII, TeX, an editable widget). The RTFReader is the Director — it parses the document. ASCIIConverter, TeXConverter, and TextWidgetConverter are Builders that assemble different representations as the reader feeds them tokens. Adding a new output format only requires a new Builder — the reader never changes.
Both create complex objects. The key difference: Builder constructs step-by-step under director control and returns the product as the final step. Abstract Factory returns a product immediately and focuses on families of products. Builder gives you finer control over the construction process and the product's internal structure.
- SQL query builders in ORMs (Hibernate Criteria API, SQLAlchemy, LINQ)
- HTTP request builders (Java's HttpClient.newBuilder(), Python's requests.Request)
- Document generation (HTML, PDF, or Word from one content model)
- Configuration objects with dozens of optional settings (Java's StringBuilder, Kotlin's DSL builders)
- Game level/map creation pipelines assembling terrain, objects, and NPCs
- Pizza-ordering systems: same steps (set size, crust, toppings) produce different pizzas
A framework must instantiate classes specific to an application, but it only knows about abstract classes. Factory Method solves this by defining a virtual creation operation — a factory method — that subclasses override to return an appropriate instance.
Classic example: a document-editing framework with abstract Application and Document classes. Application knows when to create a document but not what kind. The abstract createDocument() factory method lets DrawingApplication and SpreadsheetApplication each return their own document type. The framework code stays untouched.
- Java's
Calendar.getInstance()returns the right subtype for the locale - Spring Framework's
BeanFactory— creates beans via factory methods - Logger factories (log4j's
LogManager.getLogger()) - Plugin/extension loading systems — a host application lets plug-ins define what objects to create
- Test frameworks creating mock objects based on the test class
- React hooks as a form of factory method for creating stateful logic
Sometimes the class to instantiate is specified only at runtime, or you want to avoid a parallel hierarchy of factory classes. Prototype lets you clone an existing object (the prototype) instead. Each class implements a clone() operation. Clients ask a prototype to clone itself rather than calling a constructor.
Classic example: a music-score editor where each tool in the palette is a GraphicTool initialized with a different prototype (a quarter note, half note, etc.). Clicking a tool clones its prototype and adds it to the score. One GraphicTool class handles all music objects — no proliferation of subclasses needed.
Shallow vs. deep copy: A shallow copy shares references with the original; a deep copy recursively clones all referenced objects. Complex structures usually require deep copies to keep the clone independent.
Prototype manager: When prototypes are dynamic, a registry (associative store) lets you register/look up prototypes by key without hardcoding anything.
- JavaScript's object prototype chain — new objects inherit from prototypes
- Copy-paste in graphical editors (clone the selected shapes)
- Game: spawning enemy waves by cloning a template enemy with different configurations
- Virtual machine snapshots — a VM image is a prototype cloned into new instances
- Document templates — a template document is cloned to start a new document
- Python's
copy.deepcopy()is a language-level prototype mechanism
Some classes should have exactly one instance — a printer spooler, a filesystem, a configuration store, a window manager. Ordinary global variables don't prevent multiple instantiations. Singleton makes the class itself responsible for its sole instance: the constructor is private, and a static method returns the unique instance (creating it lazily on first access).
Instance() class operation. Maintains a pointer/reference to the sole instance. Constructor is protected or private.- Application configuration objects read once at startup
- Logger instances (e.g., Python's
logging.getLogger(name)is registry-based singleton-like) - Connection pools — a single pool manages all database connections
- Cache managers — one cache object shared across the application
- Hardware device drivers — only one instance should talk to the physical device
- Thread pools or task executors in concurrent applications
Structural Patterns
Structural patterns are concerned with how classes and objects are composed to form larger structures. Class-level structural patterns use inheritance to compose interfaces or implementations. Object-level structural patterns describe ways to assemble objects at runtime — gaining the flexibility to change compositions dynamically.
The key insight is that structure and composition are distinct from behavior. These patterns help you manage the relationship between parts and wholes, control access to objects, adapt mismatched interfaces, and share state efficiently.
A recurring challenge they address: as systems grow, class hierarchies balloon. These patterns offer flexible, composition-based alternatives that keep hierarchies manageable.
Key Themes
- Compose objects to form larger structures
- Favor object composition over class inheritance
- Provide levels of indirection to objects
- Decouple abstractions from implementations
- Adapt mismatched interfaces without modifying code
You have an existing class with the right behavior but the wrong interface. Rather than modify the class (you may not own the source) or duplicate its logic, an Adapter wraps it to expose the expected interface. There are two forms:
Class Adapter uses multiple inheritance — publicly inherits the target interface, privately inherits the adaptee's implementation. Works only for adapting a single class.
Object Adapter wraps an adaptee instance via composition — more flexible, works with the adaptee and all its subclasses.
- Java's
Arrays.asList()adapts a plain array to the List interface - JDBC drivers adapt vendor-specific database APIs to the standard Java SQL interface
- Power adapters converting electrical plugs (physical-world analogy)
- REST API wrappers adapting a third-party service's HTTP responses to your domain model
- Legacy system integration — adapting an old SOAP service to a new REST interface
- Input readers in compilers adapting file/string/stream sources to a uniform token stream
When both an abstraction and its implementation must be extensible by subclassing, inheritance alone leads to a cartesian-product explosion of subclasses. Bridge solves this by putting the abstraction and the implementation in separate class hierarchies, connected by a reference (the "bridge").
Classic example: a portable Window abstraction that must work on X11 and Presentation Manager, and must support multiple window kinds (application window, icon, dialog). Without Bridge, you'd need XApplicationWindow, PMApplicationWindow, XIconWindow, PMIconWindow, etc. With Bridge, Window and WindowImp are separate hierarchies — mix and match freely.
Both involve a level of indirection, but their intent differs. Adapter is applied retroactively to make two existing, incompatible classes work together. Bridge is designed upfront — knowing that both abstraction and implementation will evolve independently.
- JDBC: the
Connectionabstraction bridges to vendor-specific JDBC driver implementations - Device drivers: OS kernel abstractions bridging to hardware-specific implementations
- UI rendering: abstract shape hierarchy bridging to platform-specific drawing APIs (GDI, Core Graphics, Skia)
- Notification systems: abstract Notifier bridging to email, SMS, push notification implementations
- Cross-platform graphics engines separating high-level scene graph from low-level GPU API
When a system must deal with hierarchies where individual items and groups of items must be treated the same way, Composite defines a unified interface for both. A Leaf implements it for atomic items; a Composite implements it for containers of other components. Clients use the shared interface without caring whether they're talking to a leaf or a container.
Classic example: a drawing editor with lines, circles, and text. A Picture is a group of these shapes — but it also acts like a shape (you can move, scale, or draw it). Composite objects can contain other composites, so a group can contain groups, recursively.
- File system: files (leaves) and directories (composites) share a common interface
- DOM (Document Object Model): each node (element, text, comment) is part of a tree handled uniformly
- Organizational hierarchies: employees and managers share a common interface; a manager has direct reports
- GUI widget toolkits: buttons (leaves) and panels (composites containing buttons) are both Widgets
- Expression trees in compilers: constants, variables (leaves) and operators (composites)
- Menu systems: menu items and sub-menus share a common interface
Subclassing is the traditional way to add behavior — but if you have many independent extensions that can be combined freely, the number of subclasses explodes (e.g., ScrollableTextView, BorderedTextView, ScrollableBorderedTextView …). Decorator wraps an object in a conforming wrapper that adds behavior before/after forwarding requests, and wrappers can be stacked arbitrarily.
Classic example: a text view that can optionally have a border and/or scrollbars. Rather than subclassing for each combination, BorderDecorator and ScrollDecorator wrap any Component, adding their embellishment and delegating to the inner component. Stacking them gives you any combination at runtime.
Both extend behavior, but Decorator does so dynamically — you can change an object's responsibilities at runtime. Inheritance is static and applies to all instances of the class. Decorator is also more granular and composable: two decorators can be combined independently.
- Java I/O streams:
new BufferedReader(new InputStreamReader(new FileInputStream(...))) - Python function decorators:
@cache,@retry,@logstacked on any function - Web middleware pipelines: authentication → logging → rate-limiting → compression wrapping a request handler
- Coffee ordering: Espresso + Milk + WhipCream — each add-on decorates the base drink
- React Higher-Order Components (HOCs) adding behavior to any component
- HTTP response decorators adding caching, compression, or security headers
Complex subsystems are hard to use correctly — they have many classes, many dependencies, and subtle interaction protocols. A Facade provides a simple interface to the subsystem, shielding clients from the complexity. The Facade doesn't hide the subsystem (you can still access it directly if needed), but provides a convenient default view.
This is especially valuable when you want to layer a system — higher-level layers use subsystems only through their facades. Layering minimizes dependencies between subsystems, making the system easier to evolve.
- Compiler front-end: one
Compiler.compile(source)call hides lexer, parser, semantic analyzer, code generator - Home theater system: one remote "Watch Movie" button coordinates projector, stereo, lights, Blu-ray player
- Service layer in layered architecture: controllers talk to services, not repositories/DAOs directly
- SDK/library APIs simplifying complex platform features (e.g., a Camera facade hiding complex AVFoundation APIs)
- E-commerce checkout: one
placeOrder()method handles inventory, payment, shipping, notifications - React's
useContexthook acting as a facade over the complex Context API machinery
When an application uses huge numbers of similar fine-grained objects, the memory overhead per object is prohibitive. Flyweight separates intrinsic state (shared, context-independent) from extrinsic state (context-dependent, passed in at call time). One shared flyweight object represents many logical objects; clients supply the extrinsic state when needed.
Classic example: a text editor treating every character as an object. There are only ~100 characters in ASCII but millions of character objects in a document. Flyweight shares one object per unique character (its font/glyph data is intrinsic); the position in the document (extrinsic) is supplied by the row/column data structure.
- Game particle systems: thousands of bullets/sparks sharing mesh/texture data, with position as extrinsic state
- Font rendering: glyph data is shared; position on screen is extrinsic
- Java's
Integer.valueOf()caches instances for -128 to 127 - String interning (Python, Java): identical string literals share one object
- Map tile systems: tile image data is shared; map coordinates are extrinsic
- Connection pool objects: shared connection configurations with per-request session state extrinsic
A proxy provides the same interface as the real subject but adds a level of indirection. Common uses:
Virtual Proxy: Creates expensive objects on demand (lazy initialization). Example: a large image is represented by a placeholder proxy that only loads the real image when it is actually drawn.
Remote Proxy: Provides a local representative for an object in a different address space (e.g., a stub in RPC/RMI).
Protection Proxy: Controls access to an object based on permissions. Checks caller rights before forwarding.
Smart Reference: Performs additional actions when an object is accessed — counting references, locking, logging.
Both wrap an object and conform to the same interface. The intent differs: Proxy controls access to the subject (who, when, if). Decorator adds responsibilities (what). A proxy doesn't change the subject's interface or add new functionality — it manages access to the same functionality.
- Java RMI stubs — proxy objects in the client represent remote server objects
- Hibernate lazy loading — proxy objects load database records on first access
- CDN (Content Delivery Network) — a CDN edge server is a caching proxy for the origin
- API gateways proxying requests to microservices with auth, rate limiting, and routing
- JavaScript's
Proxyobject intercepts and redefines operations on objects - Security firewalls acting as access-control proxies for network resources
Behavioral Patterns
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They describe not just patterns of objects or classes, but also the patterns of communication between them — characterizing complex control flow that's difficult to follow at runtime.
These patterns shift your focus away from control flow to the way objects are interconnected. Class-level behavioral patterns use inheritance to distribute behavior (Template Method, Interpreter). Object-level patterns use composition and delegation, letting peer objects cooperate to perform tasks that no single object can carry out alone.
A central theme: encapsulate what varies — an algorithm, a request, a state, an operation — in an object, and let it vary independently.
Key Themes
- Encapsulate algorithms and requests as objects
- Decouple senders from receivers
- Define object interaction protocols
- Distribute responsibilities flexibly
- Support undo, traversal, and state management
A request needs to be processed, but the sender doesn't know which object should handle it, or multiple objects may be candidates. Chain of Responsibility links handlers in a chain; each handler either processes the request or forwards it to the next. The sender remains blissfully unaware of who handled it.
Classic example: a context-sensitive help system. Clicking a Print button should show help specific to that button; if none exists, the dialog's help; if none, the application's general help. The button, dialog, and application form a chain — each handles the request if it can, otherwise passes it up.
- Event propagation in browsers: click events bubble from child to parent elements
- Java Servlet filters and Spring interceptor chains processing HTTP requests
- Exception handling: try/catch blocks form a chain of responsibility
- Logging levels: DEBUG → INFO → WARN → ERROR handlers
- Approval workflows: expense claim goes to team lead → manager → CFO until approved
- ATM cash dispensing: tries to dispense with $100 bills, then $20, then $10, etc.
A UI button doesn't know what to do when clicked — that depends entirely on context. Instead of hard-coding the action, a Command object encapsulates the request: who receives it, which operation to invoke, and with what arguments. The button just calls execute().
This separation enables: (1) parameterizing actions at runtime, (2) queuing commands for later execution, (3) logging for crash recovery, and most powerfully, (4) undo/redo by reversing execute() with an unexecute() and maintaining a history stack.
- Text editor undo/redo — every edit (insert, delete, format) is a Command on a history stack
- GUI menu items and toolbar buttons parameterized with Command objects
- Database transactions — a command encapsulates operations that commit or rollback atomically
- Message queues — commands serialized and consumed by workers asynchronously
- Macro recording — recording a sequence of user actions as a replayable list of commands
- Remote controls — each button is a Command bound to a device action
- Job schedulers — scheduled tasks as Command objects executed at a later time
When a type of problem recurs frequently enough that it's worth expressing instances as sentences in a simple language, Interpreter represents the grammar as a class hierarchy and builds an abstract syntax tree (AST) from those classes. Each class in the hierarchy implements an interpret() operation. Evaluating the tree interprets the expression.
Classic example: regular expressions. Define grammar classes for LiteralExpression, AlternationExpression, SequenceExpression, RepetitionExpression. A regex like raining & (dogs | cats)* becomes an AST. Calling interpret(inputString) on the root checks for a match.
Use when: the grammar is simple and sentences in the language need to be interpreted frequently. The class hierarchy maps directly to grammar rules, making it easy to extend.
Avoid when: the grammar is complex (classes proliferate, maintenance becomes painful — use parser generators like ANTLR instead) or performance is critical (interpret AST directly is slower than compiled approaches).
- SQL parsers and query evaluators interpreting SQL expressions
- Regular expression engines
- Mathematical expression evaluators (calculators, formula engines)
- Configuration language interpreters (JSON/YAML schema evaluation)
- Scripting language interpreters embedded in applications
- Rule engines evaluating business rules expressed in a simple DSL
An aggregate (list, tree, set) should expose its elements for traversal without revealing its internal data structure. It should also support multiple simultaneous traversals and different traversal strategies (forward, reverse, filtered). Iterator takes traversal responsibility out of the aggregate and into a separate iterator object.
Key operations: First(), Next(), IsDone(), CurrentItem(). The aggregate provides a factory method to create iterators, keeping traversal decoupled from the data structure.
- Java's
Iterator<T>and the enhanced for-each loop (Iterable interface) - Python's iterator protocol and generators (
yieldcreates lazy iterators) - JavaScript's iterable protocol (
Symbol.iterator) used by for…of loops - Database cursor — iterating over query results without loading all rows into memory
- File system walkers traversing directory trees
- Stream processing pipelines (Java Streams, .NET LINQ, Kotlin Sequences)
When many objects interact with many other objects, the resulting tangle of references makes the system hard to reuse and change. A Mediator centralizes and controls all interactions. Objects communicate with the mediator rather than with each other directly. This turns an N×N mesh of connections into N spokes on a hub.
Classic example: a dialog box with interdependent widgets. A checkbox enables/disables a text field; a list selection updates a button label; etc. Instead of each widget knowing about all others, they notify the DialogDirector (mediator), which coordinates the response. Each widget stays simple and reusable.
Both abstract relationships between classes. Facade is unidirectional — it simplifies the interface to a subsystem for clients. Mediator is bidirectional — it mediates interactions among the objects in the system. Facade objects don't know each other; Mediator colleagues communicate through the mediator.
- Air traffic control tower: planes communicate through the tower, not directly with each other
- Chat room server: messages between users pass through the chat server
- MVC: the Controller mediates between Model and View
- Redux/Flux store: components dispatch actions to the store; the store notifies subscribers
- UI dialog forms with complex field interdependencies
- Event bus / message broker in event-driven architectures
Undo requires saving an object's state. But saving state directly exposes internal implementation — breaking encapsulation. Memento solves this by letting the Originator create a Memento containing a snapshot of its own private state. The Caretaker holds the memento without ever accessing its contents. To undo, the Originator restores itself from the memento.
The Memento defines two interfaces: a narrow one for Caretakers (opaque, no access to contents) and a wide one for Originators (full access). This keeps state internal while externalizing it safely.
- Text editor undo — each typing action's pre-state is saved as a memento
- Game save points — full game state serialized and stored for later restoration
- Database transaction savepoints — rollback to a specific point within a transaction
- Browser history — each page visit stores navigation state for back/forward navigation
- Version control system commits — each commit is a memento of the repository state
- Wizard-style forms — saving intermediate form state to navigate back without data loss
When one object (the Subject/Model) changes state and an unknown number of other objects (Observers/Views) must be updated accordingly, hard-coding the dependencies creates tight coupling. Observer lets the Subject maintain a list of registered observers and notify all of them on change, without knowing their concrete types or count.
This is the core of MVC: the Model is the Subject; Views are Observers. A single Model change automatically refreshes all open views — a spreadsheet, a histogram, and a pie chart — simultaneously.
Push: Subject sends detailed state data in the notification. Simpler for observers but Subject must know what data observers need.
Pull: Subject sends a minimal notification; observers query the Subject for the state they need. More decoupled, but observers may make unnecessary queries.
- MVC frameworks — model notifies views of changes
- DOM event listeners —
addEventListenerregisters observers on elements - React's
useState/ Redux store subscriptions notify components to re-render - Message queues and event buses — publishers notify multiple subscribers
- Stock ticker systems — price changes notify all registered displays and alerts
- Spreadsheet cells — formula cells observe source cells and recalculate when they change
- News feeds / webhooks — systems subscribing to external event streams
When an object's behavior depends heavily on its state, the naive approach is large conditional statements scattered throughout methods. State encapsulates each state in an object and delegates state-dependent behavior to the current State object. The context delegates requests to its current state; transitioning to a new state just changes the state object reference.
Classic example: a TCP connection in states Established, Listening, Closed. Rather than conditional logic in every method, delegate to EstablishedState, ListeningState, ClosedState objects. Each handles operations differently; each handles state transitions.
Both delegate behavior to a separate object. State transitions between states automatically — the state objects know about each other. Strategy is chosen by the client and stays stable — strategies don't switch themselves. State models a lifecycle; Strategy models interchangeable algorithms.
- TCP/IP connection states (Closed, Listening, Established, Time-wait)
- Vending machine states (Idle, HasMoney, Dispensing, OutOfStock)
- Traffic light controller cycling through Red → Green → Yellow
- Document workflow states (Draft → Review → Published → Archived)
- Media player states (Playing, Paused, Stopped, Loading)
- Game character states (Idle, Walking, Jumping, Attacking, Dead)
- Order fulfillment lifecycle (Pending → Confirmed → Shipped → Delivered → Returned)
When multiple algorithms exist for a task and clients need to switch between them, hard-coding algorithm selection (or using conditionals) makes the code rigid and bloated. Strategy encapsulates each algorithm in its own class behind a common interface. The Context holds a Strategy reference and delegates the operation to it. To change the algorithm, swap the Strategy object.
Classic example: a text compositor supporting different line-breaking strategies (simple, TeX-quality, no-break). The compositor delegates to an interchangeable Compositor strategy: SimpleCompositor, TeXCompositor. Changing composition quality at runtime just swaps the strategy.
- Sorting algorithms: Java's
Comparatorpassed toCollections.sort() - Payment processing: CreditCard, PayPal, Crypto strategies implementing a common payment interface
- Data compression: LZW, Deflate, Brotli strategies behind a Compressor interface
- Navigation apps: routes via car, walking, public transit are interchangeable routing strategies
- Machine learning: different training algorithms (SGD, Adam, RMSProp) as interchangeable optimizers
- Logging: file, console, remote strategies behind a Logger interface
- Tax calculation: different tax strategies per country/region
When several classes share the same algorithm skeleton with different implementations of specific steps, the common structure can be factored into an abstract base class. The template method in the base class calls abstract "hook" operations — steps subclasses must override — while keeping the overall algorithm in one place.
Classic example: a report generator with fixed steps: open file → parse data → print header → print body → print footer → close file. The template method controls this order. Subclasses only override steps like printHeader() and printBody() to customize behavior for specific report types.
Both vary parts of an algorithm. Template Method uses inheritance — the invariant parts are in the base class, varying parts in subclasses. This is a compile-time choice. Strategy uses composition — the whole algorithm is encapsulated in a separate object, swappable at runtime. Template Method is simpler but less flexible.
- Java's
AbstractList— provides template methods; subclasses implementget()andsize() - JUnit's test lifecycle:
setUp()→test*()→tearDown()is a template method - Servlet's
HttpServlet:service()dispatches todoGet(),doPost()etc. - Build systems: compile → test → package → deploy as a template, each step overridden per language
- Report generation pipelines with fixed structure but customizable sections
- Data import pipelines: open source → validate → transform → load (ETL skeleton)
When you need to perform many distinct, unrelated operations on an object structure (a Composite tree, for example), and you don't want to pollute each element class with all those operations, Visitor externalizes the operations. Each element implements accept(Visitor) — a double dispatch mechanism that calls the right visitXxx() method on the visitor for the element's concrete type.
Classic example: a document composed of Character, Image, and Row elements. Adding spell-checking, hyphenation, and word-count operations would require modifying all element classes each time. With Visitor, each new operation is a new Visitor subclass. Element classes never change.
Visitor exploits double dispatch: the operation performed depends on both the type of the Visitor and the type of the element. When an element calls visitor.visitMyType(this), the visitor's overloaded method for that type is called. This effectively simulates method dispatch based on two types.
Use when: the object structure is stable (rarely changes) but you frequently need new operations on it. Adding a new visitor is easy; adding a new element type requires updating all visitors.
Avoid when: the class hierarchy changes frequently — every new element type requires updating every Visitor interface and all implementations.
- Compilers: AST visitors for type-checking, optimization, code generation, pretty-printing
- XML/JSON document processors visiting each node type differently
- Tax calculators visiting different types of financial instruments (stocks, bonds, real estate)
- Game engine component systems visiting entities with different component combinations
- Report generators applying different formatting operations to different document elements
- Static analysis tools scanning code ASTs for different categories of issues