Creational pattern
In software engineering, creational design patterns are a category of design patterns focused on providing flexible and efficient mechanisms for object instantiation in object-oriented programming, abstracting the creation process to decouple client code from specific classes and promote system independence from how objects are created, composed, and represented.[1] These patterns address common challenges in object creation by encapsulating instantiation logic, enabling deferred decisions on the exact classes to instantiate, and supporting variations in object construction without altering existing code structures.[2]
The concept of creational patterns was formalized in the seminal 1994 book Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, known as the "Gang of Four" (GoF), which categorized design patterns into creational, structural, and behavioral types.[3] This work established creational patterns as essential tools for enhancing code reusability, maintainability, and extensibility by handling initialization and configuration of classes and objects in stylized, generalized ways.[4] Unlike structural patterns, which focus on object composition, or behavioral patterns, which manage interactions, creational patterns specifically target the "when and how" of object creation to avoid tight coupling and improve adaptability in evolving systems.[5]
The five primary creational patterns identified by the GoF are:
These patterns are widely applied in frameworks and libraries to manage resource-intensive creations or polymorphic object hierarchies, with ongoing relevance in modern languages like Java, C++, and Python.[6]
Introduction
Overview
Creational patterns constitute one of the three primary categories of design patterns in software engineering, alongside structural and behavioral patterns, as systematized by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—collectively known as the Gang of Four (GoF)—in their seminal 1994 book Design Patterns: Elements of Reusable Object-Oriented Software.[3] This categorization provides a framework for identifying recurring solutions to common problems in object-oriented design, with creational patterns specifically addressing object instantiation.[3]
The general intent of creational patterns is to offer flexible, reusable mechanisms for constructing objects while decoupling the creation process from the objects' usage in the code, thereby enhancing maintainability and adaptability in complex systems.[3] By abstracting the instantiation logic, these patterns allow developers to vary the concrete classes involved without altering client code, promoting principles like dependency inversion and single responsibility.[3]
The five classic creational patterns identified by the GoF are Singleton, Factory Method, Abstract Factory, Builder, and Prototype, each targeting different aspects of object creation challenges.[3]
These patterns arose amid the transition from procedural to object-oriented programming paradigms during the 1980s and 1990s, a period marked by explosive growth in object-oriented technologies such as Smalltalk and C++, which emphasized abstraction and encapsulation in software design, including object instantiation.
Historical Development
The concept of design patterns in software engineering traces its roots to the late 1970s and 1980s within the Smalltalk and emerging C++ communities, where architects drew inspiration from Christopher Alexander's work on architectural patterns in his 1977 book A Pattern Language. These ideas were adapted to software by pioneers such as Ward Cunningham and Kent Beck, who began exploring patterns as reusable solutions for object-oriented design challenges in Smalltalk environments.[7][8]
A pivotal milestone occurred with the first pattern-focused workshops at the OOPSLA conferences starting in 1987, where Cunningham and Beck presented early pattern languages for user interface design and object-oriented specification. The formalization of these concepts culminated in the 1994 publication of Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—collectively known as the Gang of Four—which cataloged 23 design patterns, including five creational ones, and has sold over 500,000 copies worldwide. This book profoundly influenced the development of object-oriented languages such as Java and C#, establishing a shared vocabulary for software design.[8][9]
Following 1994, creational patterns integrated into major frameworks, notably the Spring Framework released in 2002, which leverages patterns like factories for dependency injection in Java applications, and the .NET Framework, also launched in 2002, which embeds creational mechanisms such as singletons and builders in its base class library. Adaptations for functional programming paradigms emerged in languages like Scala, where creational patterns are reinterpreted using higher-order functions and immutable data structures to align with functional principles.[10][11][12]
The influence of these patterns extended to the standardization of the Unified Modeling Language (UML) in 1997 by the Object Management Group, where pattern-based modeling notations facilitated the representation of object creation and reuse in visual diagrams.[13]
Core Concepts
Definition
Creational design patterns abstract the instantiation process, making systems independent of how their objects are created, composed, and represented.[1] These patterns, as classified by the Gang of Four in their seminal work, provide mechanisms to control object creation in a manner that enhances flexibility and decoupling in object-oriented software design.[1]
In contrast to structural patterns, which emphasize the composition of classes and objects to form larger, more flexible structures, and behavioral patterns, which address algorithms and the distribution of responsibilities among objects, creational patterns uniquely target the variability inherent in object instantiation.[14][15] This focus allows developers to vary the creation strategy without altering the client code that relies on the objects.
Key elements of creational patterns include the encapsulation of creation logic to hide complex instantiation details, support for polymorphism during object creation to allow interchangeable implementations, and the promotion of flexibility across families of related objects.[1] By centralizing these aspects, creational patterns enable systems to adapt to changing requirements in object construction without widespread modifications.
Creational patterns align with the SOLID principles, particularly the Dependency Inversion Principle, by separating object creation from the modules that depend on them, ensuring that abstractions rather than concrete implementations drive dependencies.[16] This separation fosters loose coupling and facilitates testing and maintenance in large-scale software architectures.[17]
Purpose and Motivation
Creational design patterns address fundamental challenges in object-oriented design by abstracting the process of object instantiation, making systems independent of how objects are created, composed, and represented. A core problem they solve is the tight coupling between client code and concrete classes, which arises when clients directly invoke constructors of specific classes, leading to inflexible architectures that resist changes and extensions.[18] This coupling often results in widespread dependencies that complicate maintenance and violate principles like dependency inversion, where high-level modules should not depend on low-level details.[19]
The motivation for these patterns also stems from the difficulties in handling complex object construction, such as scenarios involving numerous parameters or optional configurations, which can lead to an explosion of overloaded constructors and code duplication in initialization routines. By providing mechanisms for deferred or parameterized creation, creational patterns enable runtime selection of object types without hardcoding concrete classes in client code, thus supporting more modular and adaptable designs.[20] This approach aligns with the open-closed principle, allowing new object types to be added without altering existing client logic.
In addition to decoupling and simplification, creational patterns promote resource efficiency through controlled instantiation, such as limiting object multiplicity or enabling reuse via cloning, which avoids redundant computations in performance-sensitive applications.[19] They enhance maintainability by facilitating unit testing with mock or stub objects substituted through abstract creation interfaces, and they scale well in large systems like graphical user interfaces—where dynamic component assembly is common—or database environments requiring managed resource pools.[18] Without such patterns, developers frequently encounter pitfalls like brittle code that breaks under extension and repeated initialization logic scattered across the codebase, increasing development time and error rates.[19]
Types
Singleton
The Singleton pattern is a creational design pattern that ensures a class has only one instance while providing a global point of access to it, and it takes responsibility for creating and managing that instance. This approach centralizes control over the instantiation process, preventing multiple instances from being created inadvertently, which is particularly useful in scenarios requiring a unified resource handler. The pattern was formalized in the seminal work on design patterns, where it is described as a mechanism to guarantee uniqueness and facilitate controlled access across an application.[21]
Structurally, the Singleton is typically implemented using a private constructor to block external instantiation, a static class variable to store the sole instance, and a public static method—commonly named getInstance()—that returns the instance while creating it lazily if it does not yet exist. This design enforces the single-instance constraint at the class level, allowing the instance to be referenced globally without risking duplication. In multithreaded environments, variations address concurrency issues; for instance, double-checked locking optimizes performance by first checking the instance without acquiring a lock and then using synchronization only if necessary, reducing overhead while ensuring thread safety.[21][22] Additionally, in Java, an enum-based implementation is favored for its inherent thread safety, serialization protection, and simplicity, as recommended by Joshua Bloch in the second edition of Effective Java published in 2008.[23][24]
The pattern finds applicability in managing shared resources that benefit from a single, centralized instance, such as loggers for consistent event recording, caches to avoid redundant data storage, and configuration managers to maintain uniform settings across an application. These use cases leverage the global access point to promote efficiency and coherence without the overhead of multiple instances competing for resources.[25][26]
Despite its utility, the Singleton pattern introduces known issues, primarily through its implicit global state, which can foster tight coupling between components and obscure dependencies, complicating maintenance and scalability. Furthermore, the hidden dependencies it creates make unit testing challenging, as mocking or isolating the singleton for independent test cases often requires additional frameworks or workarounds to reset or substitute the instance between tests.[27][28]
Factory Method
The Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate, thereby enabling a class to defer instantiation to its subclasses.[3] This approach promotes the open-closed principle by allowing extensions through subclassing without altering the original class's code.[3]
In its structure, the pattern involves a Creator class that declares an abstract factory method responsible for returning an instance of a Product interface or superclass. Concrete subclasses of the Creator override this factory method to instantiate specific concrete Product classes, while the client code interacts solely with the Product interface, ensuring loose coupling.[3] For example, in pseudocode:
abstract class Creator {
abstract Product factoryMethod();
void someOperation() {
Product product = factoryMethod();
// Use product
}
}
class ConcreteCreator extends Creator {
Product factoryMethod() {
return new ConcreteProduct();
}
}
interface Product {
void operation();
}
class ConcreteProduct implements Product {
void operation() {
// Product-specific behavior
}
}
abstract class Creator {
abstract Product factoryMethod();
void someOperation() {
Product product = factoryMethod();
// Use product
}
}
class ConcreteCreator extends Creator {
Product factoryMethod() {
return new ConcreteProduct();
}
}
interface Product {
void operation();
}
class ConcreteProduct implements Product {
void operation() {
// Product-specific behavior
}
}
This structure encapsulates object creation within the factory method, leveraging polymorphism to vary the product type at runtime based on the Creator subclass.[3]
The pattern is applicable in scenarios where a class cannot anticipate the exact type of objects it must create in advance, or where client code should remain independent of concrete classes to enhance flexibility in frameworks.[29] A common use case is in graphical user interface (GUI) applications, such as document processing software where an abstract Application class defines a factory method to create Document objects, and subclasses like DrawingApplication or TextApplication override it to produce specific document types (e.g., DrawingDocument or TextDocument) without the client knowing the concrete implementations.[29]
Historically, the Factory Method has evolved as a foundational creational pattern since its formalization in the 1994 Gang of Four book, finding widespread adoption in object-oriented frameworks.[3] It is prominently used in Java's JDBC drivers, where the DriverManager class employs a factory-like mechanism to instantiate database-specific Connection objects based on the provided driver, allowing applications to work with various databases without direct dependency on vendor-specific classes.[30] This contrasts with the simpler "simple factory" approach, which centralizes creation logic in a non-inheritable static method but lacks the extensibility of subclasses and is not considered a full design pattern.[29]
A key drawback of the Factory Method is the potential for subclass proliferation: each new product variant requires a corresponding Creator subclass, which can increase the system's class count and complexity if not managed carefully.[3]
Abstract Factory
The Abstract Factory pattern is a creational design pattern that enables the creation of families of related or dependent objects without specifying their concrete classes, promoting flexibility and consistency across product variants.[31][32] It achieves this by defining an abstract interface for factories, allowing clients to work with product hierarchies through polymorphism rather than direct instantiation. This pattern is particularly useful in scenarios where multiple object types must interoperate seamlessly within themed groups, such as graphical user interface components tailored to different operating systems.[31]
The intent of the Abstract Factory pattern is to encapsulate a group of individual factories sharing a common theme, thereby ensuring that the created products are compatible and interchangeable within their families.[33] For instance, it allows the production of cohesive sets like furniture styles (e.g., modern or Victorian chairs and sofas) or UI elements (e.g., Windows-style buttons and menus), where mixing incompatible variants would break functionality. By isolating concrete implementations behind abstract interfaces, the pattern supports runtime selection of factory types, enhancing code maintainability and extensibility.[31]
In terms of structure, the pattern involves several key components: an AbstractFactory interface declaring methods for creating each type of abstract product; ConcreteFactory subclasses that implement the AbstractFactory to generate specific product families; AbstractProduct interfaces defining the product types; and ConcreteProduct classes implementing those interfaces for particular variants. Clients interact solely with the abstract layers, invoking factory methods like createButton() or createMenu() without knowledge of the underlying concrete classes. This composition often integrates with the Factory Method pattern, where individual concrete factories may defer product creation to subclasses via factory methods.[32][31]
The pattern is applicable in domains requiring consistent families of objects, such as developing cross-platform UI toolkits—where a WinFactory might produce Windows-compatible buttons and scrollbars, while a MacFactory creates macOS equivalents—or database access layers, where factories generate connections, statements, and result sets optimized for specific vendors like MySQL or Oracle.[31] It is especially relevant when a system must support multiple product configurations without tying client code to concrete implementations, or when evolving parallel hierarchies of products.[32]
One challenge with the Abstract Factory pattern is that introducing a new product type necessitates modifications to the AbstractFactory interface and all concrete factories, potentially violating the open-closed principle and increasing maintenance overhead.[31] Despite this, its use of composition over inheritance distinguishes it from simpler factory approaches, providing a robust mechanism for managing object family creation in complex, multi-variant systems.[32]
Builder
The Builder pattern is a creational design pattern that separates the construction of a complex object from its representation, enabling the same construction process to create different representations. Its primary intent is to construct complex objects step by step, mitigating issues such as telescoping constructors that arise when classes have many optional parameters, thereby promoting immutability and readability in object creation.
In terms of structure, the pattern typically involves four key components: a Builder interface or abstract class defining methods for constructing the product's parts; one or more ConcreteBuilder classes implementing the Builder to create specific representations of the product; a Product class representing the complex object being built; and an optional Director class that orchestrates the building process by invoking the Builder's methods in a specific order. This separation allows clients to direct construction without knowing the details of internal representation, as the Director (if used) encapsulates the construction algorithm while delegating assembly to the Builder.
The pattern is applicable when the creation of a complex object requires an algorithm independent of the object's parts and their assembly, such as building documents where the same components can yield different formats like HTML or PDF. It is also suitable for configuration objects with numerous optional parameters, where traditional constructors become unwieldy, as seen in scenarios involving step-by-step initialization of immutable objects with varying attributes.
A common variant is the fluent interface Builder, which enhances readability by chaining method calls that return the Builder instance itself, popularized in libraries such as Apache Commons Configuration for setting up complex configurations declaratively.[34] This approach, inspired by the core Builder but without a mandatory Director, allows expressive code like builder.setOption1().setOption2().build(), reducing verbosity while maintaining step-by-step construction.
While effective for complex objects, the Builder pattern introduces trade-offs, including increased code overhead—requiring additional classes and methods—which may not justify its use for simple objects with few parameters, potentially complicating maintenance without proportional benefits in flexibility or immutability.
The Prototype pattern is a creational design pattern that enables the creation of new objects by cloning an existing instance, known as the prototype, rather than instantiating classes directly through constructors. This approach specifies the types of objects to be created using prototypical instances and leverages copying to produce new objects, avoiding the need for subclassing the Creator class or knowing the exact classes at compile time. It is particularly beneficial when the classes to instantiate are not specified until runtime or when direct instantiation is prohibitively costly due to complex initialization processes.[35]
In terms of structure, the pattern involves a Prototype abstract interface or base class that declares a clone() operation for producing copies of itself. Concrete implementations, such as ConcretePrototype, provide the actual cloning logic, which can result in either a shallow copy—duplicating only primitive fields and references—or a deep copy, recursively cloning all nested objects to ensure independence. A Client maintains a registry of prototypes and uses the clone() method to create instances, customizing them as needed after cloning. This structure promotes flexibility by decoupling object creation from specific class knowledge.[35]
The pattern finds applicability in scenarios requiring efficient duplication of complex objects, such as in graphics editing software where shapes like circles or rectangles are cloned to create duplicates without reinitializing properties. For instance, in a document editor, graphical elements (glyphs) can be prototyped and cloned to populate new documents rapidly. In game development, it supports object pooling by cloning pre-initialized entities, such as enemies or projectiles, to reuse resources and avoid repeated expensive setups like loading textures or computing initial states.[35][36][37]
Implementation often relies on language-specific mechanisms, but pitfalls exist; in Java, the Cloneable interface serves as a marker to enable Object.clone(), yet it defaults to shallow copying and throws exceptions if not implemented properly, requiring overrides for deep copies. Alternatives include copy constructors, which take an instance of the same class as input and explicitly copy fields, offering more control and avoiding the Cloneable interface's issues like protected access to clone(). These methods ensure clones are independent but demand careful handling to update for new fields.[38][39]
A key limitation arises when prototypes contain shared mutable state, such as references to common objects; shallow copies may lead to unintended modifications propagating across clones, while deep copies mitigate this but increase overhead and complexity for deeply nested structures. Developers must decide on copy depth based on object composition to prevent bugs from aliasing.[40]
Applications
Usage Scenarios
Creational design patterns are applied in software development to address specific challenges in object instantiation, such as ensuring uniqueness, enabling extensibility, maintaining consistency across related objects, supporting step-by-step construction, and optimizing for performance in resource-heavy environments.
The Singleton pattern is frequently used for managing shared resources like database connections, where a single instance prevents redundant connections and ensures thread-safe access across an application.[41] In plugin architectures, the Factory Method pattern facilitates the creation of extensible components by delegating instantiation to subclasses, allowing new plugins to be integrated without altering the main codebase, as demonstrated in .NET Core plugin systems.[42] The Abstract Factory pattern is employed in cross-platform graphical user interfaces (GUIs) to generate families of platform-specific widgets, such as buttons and scrollbars for Windows or macOS, decoupling the application logic from concrete implementations.[31] For building complex API requests, the Builder pattern enables fluent, step-by-step assembly of objects with optional parameters while enforcing immutability, as implemented in the OkHttp library for HTTP client configuration. The Prototype pattern proves useful in simulations and game development, where cloning initialized objects—such as game entities—avoids the computational cost of repeated setups from scratch.[37]
Choosing among creational patterns hinges on contextual needs: the Singleton ensures a single global instance for resources requiring uniqueness, like configuration managers; the Factory Method is selected when subclassing must override creation logic for future extensions; the Abstract Factory promotes uniformity when producing interdependent object families; the Builder is ideal for immutable complex objects with variable configurations to avoid telescoping constructors; and the Prototype is preferred when duplicating existing objects is cheaper than full initialization.
These patterns can integrate with one another to enhance flexibility; for example, an Abstract Factory may produce Builder instances for constructing configurable families of objects, combining family consistency with detailed customization.[43]
In contemporary applications, creational patterns support microservices architectures through dependency injection frameworks like Dagger, which leverages Factory Method variants to provision and inject objects dynamically across services.[44]
Benefits and Limitations
Creational design patterns offer significant advantages in software design by abstracting object creation processes, which enhances system flexibility and allows for easier swapping of implementations without altering client code.[45] This abstraction promotes adherence to object-oriented principles such as single responsibility, as the creation logic is encapsulated separately from the business logic, leading to cleaner and more modular codebases.[46] Empirical studies confirm that applying creational patterns reduces coupling metrics like fan-in and fan-out, supporting greater adaptability in evolving systems.[47]
Another key benefit is improved testability, as these patterns facilitate the injection of mocks or stubs during unit testing, decoupling tests from concrete implementations. For instance, refactoring to creational patterns like factory method or dependency injection has been shown to boost design testability by up to 19.11% in open-source Java projects.[48] Overall, they contribute to better code quality and reusability, with systematic reviews of 50 studies from 2000–2018 indicating positive impacts on maintainability through structured object instantiation.[47]
Despite these gains, creational patterns introduce limitations, primarily through added complexity and boilerplate code, as they often require additional classes and methods for creation logic, increasing source lines of code (SLOC) for patterns like Builder and Abstract Factory.[49] This can lead to performance overhead, particularly in patterns like Prototype, where deep cloning of objects incurs computational costs unsuitable for resource-intensive scenarios.
In simple applications, applying these patterns risks over-engineering, violating the YAGNI principle by introducing unnecessary abstractions that complicate maintenance without proportional benefits.[50] Empirical evidence highlights mixed outcomes, where while complexity metrics like cyclomatic complexity decrease (e.g., for the Builder pattern), the increased class count and SLOC can elevate fault proneness if patterns decay over time.[49][46]
The trade-offs of creational patterns thus center on balancing short-term development overhead against long-term maintainability gains; they excel in large, dynamic systems but may hinder agility in small projects, as evidenced by varying resilience to changes in pattern-adopting codebases.[46]
Implementation
Structural Diagrams
Creational design patterns are typically represented using UML class diagrams that highlight the static relationships between classes and interfaces involved in object creation, focusing on abstraction to promote flexibility and decoupling. These diagrams commonly feature creator classes or interfaces responsible for instantiation, product classes defining the created objects, and associations showing how creators return or compose products without exposing concrete implementation details. Arrows denote key relationships such as generalization (inheritance) with a solid line and hollow triangle arrowhead, association with a solid line, and dependency with a dashed line and open arrowhead.[51][45]
For the Singleton pattern, the UML diagram consists of a single class with a private constructor to prevent external instantiation, a static method (often named getInstance) for accessing the sole instance, and potentially a static attribute referencing the instance itself, illustrated by a self-referencing association or note. This simple structure emphasizes the class's internal control over its instantiation without inheritance or external dependencies.[52]
The Factory Method pattern's diagram includes an abstract Creator class with an abstract factoryMethod operation returning a Product interface, a ConcreteCreator subclass that implements the factoryMethod to return a ConcreteProduct, and the Product interface with its ConcreteProduct implementation; a generalization arrow connects Creator to ConcreteCreator, while a return-type dependency or association links the factoryMethod to Product. This layout visualizes how subclasses override the creation logic while adhering to a common interface.[52][29]
In the Abstract Factory pattern, the diagram shows an AbstractFactory interface with abstract operations like createProductA and createProductB, multiple ConcreteFactory implementations (e.g., ConcreteFactory1 and ConcreteFactory2) each providing those operations returning corresponding ConcreteProduct variants (e.g., ProductA1 and ProductB1 for one family, ProductA2 and ProductB2 for another), and AbstractProductA/B interfaces; realization arrows from ConcreteFactories to AbstractFactory, and associations from the create operations to the respective products form a grid-like structure representing families of related objects.[52]
The Builder pattern diagram features a Builder interface with abstract buildPart operations and a getResult method returning the Product class, a ConcreteBuilder implementing the Builder with concrete build operations that compose the Product step-by-step, and optionally a Director class with a construct method that depends on the Builder interface to orchestrate the building process; associations connect Director to Builder, and Builder methods to Product attributes, illustrating a chain of construction responsibilities.[52]
For the Prototype pattern, the UML diagram includes a Prototype interface with a clone operation returning the Prototype type, one or more ConcretePrototype classes implementing clone to return a copy of themselves, often shown with a self-arrow indicating the cloning relationship; generalization arrows link ConcretePrototypes to Prototype, emphasizing the reuse of instances through duplication rather than full recreation.[52]
Common notations in these UML 2.0 diagrams include italicized names for abstract classes or operations to denote abstraction, the <> stereotype for interfaces, and multiplicity indicators (e.g., 1 for single instances in Singleton) on associations; variations arise across languages, such as using interfaces explicitly in Java diagrams versus pure abstract classes in C++ to represent polymorphism. These elements standardize the visualization of pattern structures for cross-language applicability.[53][54]
Such diagrams aid understanding by visually decoupling clients from concrete classes, showing how interactions occur solely through abstract creators—for instance, a client depends only on the Creator interface in Factory Method, allowing substitutions without altering client code and highlighting the patterns' role in flexible object creation.[45][51]
Singleton
The Singleton pattern ensures a class has only one instance and provides a global point of access to it, often implemented with lazy initialization to defer object creation until needed. A thread-safe version uses a mutex to prevent multiple instances in concurrent environments.[26]
Here is language-agnostic pseudocode for a lazy-initialized Singleton, such as a database connection manager:
class Database {
private static Database instance = null;
private static mutex lock;
private Database() {
// Initialization code
}
public static Database getInstance() {
if (instance == null) {
lock.lock();
if (instance == null) {
instance = new Database();
}
lock.unlock();
}
return instance;
}
public void query(String sql) {
// Execute query logic
}
}
class Database {
private static Database instance = null;
private static mutex lock;
private Database() {
// Initialization code
}
public static Database getInstance() {
if (instance == null) {
lock.lock();
if (instance == null) {
instance = new Database();
}
lock.unlock();
}
return instance;
}
public void query(String sql) {
// Execute query logic
}
}
In execution, a client calls Database.getInstance(); if the instance is null, the mutex is acquired, a new Database is created only once, and the lock is released before returning the singleton. Subsequent calls return the existing instance without recreation. In languages like Java, adaptations include using the synchronized keyword on getInstance() or enums for inherent thread safety, as enums provide a single static instance by design.[55]
Factory Method
The Factory Method pattern defines an interface for creating an object but allows subclasses to decide which class to instantiate, deferring instantiation to subclasses. A common example involves an application creating documents via a virtual method.[29]
Language-agnostic pseudocode illustrates this with an Application base class and document creation:
// Product interface
interface Document {
void render();
void save();
}
// Concrete products
class PDFDocument implements Document {
public void render() { /* PDF rendering */ }
public void save() { /* PDF saving */ }
}
class WordDocument implements Document {
public void render() { /* Word rendering */ }
public void save() { /* Word saving */ }
}
// Creator (Factory)
abstract class Application {
abstract [Document](/page/Document) create[Document](/page/Document)();
void open[Document](/page/Document)() {
[Document](/page/Document) doc = create[Document](/page/Document)();
doc.[render](/page/Render)();
}
}
// Concrete creators
class PDFApplication extends Application {
[Document](/page/Document) create[Document](/page/Document)() {
return new PDF[Document](/page/Document)();
}
}
class WordApplication extends Application {
Document createDocument() {
return new WordDocument();
}
}
// Product interface
interface Document {
void render();
void save();
}
// Concrete products
class PDFDocument implements Document {
public void render() { /* PDF rendering */ }
public void save() { /* PDF saving */ }
}
class WordDocument implements Document {
public void render() { /* Word rendering */ }
public void save() { /* Word saving */ }
}
// Creator (Factory)
abstract class Application {
abstract [Document](/page/Document) create[Document](/page/Document)();
void open[Document](/page/Document)() {
[Document](/page/Document) doc = create[Document](/page/Document)();
doc.[render](/page/Render)();
}
}
// Concrete creators
class PDFApplication extends Application {
[Document](/page/Document) create[Document](/page/Document)() {
return new PDF[Document](/page/Document)();
}
}
class WordApplication extends Application {
Document createDocument() {
return new WordDocument();
}
}
The execution flow begins when the client initializes the application based on configuration (e.g., "PDF" selects PDFApplication); calling openDocument() invokes the overridden createDocument(), which returns a concrete Document instance without the client knowing its type; the document is then rendered or saved polymorphically. This pattern facilitates testing by providing mock factories that return test doubles instead of real objects.[29]
Abstract Factory
The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes, ensuring compatibility within families. An example uses a GUI factory to produce platform-specific UI components like buttons.[31]
Language-agnostic pseudocode for a GUIFactory creating buttons and checkboxes:
// Abstract products
[interface](/page/Interface) Button {
void paint();
}
interface Checkbox {
void paint();
}
// Concrete products for Windows
class WinButton implements Button {
public void paint() { /* Windows button paint */ }
}
class WinCheckbox implements Checkbox {
public void paint() { /* Windows checkbox paint */ }
}
// Concrete products for macOS
class MacButton implements Button {
public void paint() { /* macOS button paint */ }
}
class MacCheckbox implements Checkbox {
public void paint() { /* macOS checkbox paint */ }
}
// Abstract factory
interface GUIFactory {
[Button](/page/Button) createButton();
[Checkbox](/page/Checkbox) createCheckbox();
}
// Concrete factories
class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public Checkbox createCheckbox() { return new WinCheckbox(); }
}
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
// Abstract products
[interface](/page/Interface) Button {
void paint();
}
interface Checkbox {
void paint();
}
// Concrete products for Windows
class WinButton implements Button {
public void paint() { /* Windows button paint */ }
}
class WinCheckbox implements Checkbox {
public void paint() { /* Windows checkbox paint */ }
}
// Concrete products for macOS
class MacButton implements Button {
public void paint() { /* macOS button paint */ }
}
class MacCheckbox implements Checkbox {
public void paint() { /* macOS checkbox paint */ }
}
// Abstract factory
interface GUIFactory {
[Button](/page/Button) createButton();
[Checkbox](/page/Checkbox) createCheckbox();
}
// Concrete factories
class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public Checkbox createCheckbox() { return new WinCheckbox(); }
}
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
In execution, the client selects a factory at runtime (e.g., based on OS: Windows uses WinFactory); calls like factory.createButton().paint() produce a matching family of components (e.g., all Windows-style), ensuring consistency without coupling to concrete classes. For unit tests, abstract factories allow injecting mock families to isolate UI logic.[31]
Builder
The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It uses a fluent interface for step-by-step building, as in constructing a customizable pizza.[43]
Language-agnostic pseudocode for a PizzaBuilder:
// Product
class Pizza {
private String dough;
private String sauce;
private String topping;
// Getters for parts
public String getDough() { return dough; }
public String getSauce() { return sauce; }
public String getTopping() { return topping; }
}
// Builder interface
interface PizzaBuilder {
PizzaBuilder setDough(String dough);
PizzaBuilder setSauce(String sauce);
PizzaBuilder setTopping(String topping);
Pizza build();
}
// Concrete builder
class PizzaBuilder implements PizzaBuilder {
private Pizza pizza = new Pizza();
public PizzaBuilder setDough(String dough) {
pizza.dough = dough;
return this;
}
public PizzaBuilder setSauce(String sauce) {
pizza.sauce = sauce;
return this;
}
public PizzaBuilder setTopping(String topping) {
pizza.topping = topping;
return this;
}
public Pizza build() {
return pizza;
}
}
// Product
class Pizza {
private String dough;
private String sauce;
private String topping;
// Getters for parts
public String getDough() { return dough; }
public String getSauce() { return sauce; }
public String getTopping() { return topping; }
}
// Builder interface
interface PizzaBuilder {
PizzaBuilder setDough(String dough);
PizzaBuilder setSauce(String sauce);
PizzaBuilder setTopping(String topping);
Pizza build();
}
// Concrete builder
class PizzaBuilder implements PizzaBuilder {
private Pizza pizza = new Pizza();
public PizzaBuilder setDough(String dough) {
pizza.dough = dough;
return this;
}
public PizzaBuilder setSauce(String sauce) {
pizza.sauce = sauce;
return this;
}
public PizzaBuilder setTopping(String topping) {
pizza.topping = topping;
return this;
}
public Pizza build() {
return pizza;
}
}
The flow starts with a client creating a PizzaBuilder instance; fluent methods like setDough("thin").setSauce("tomato").setTopping("cheese") chain to configure parts step-by-step; build() assembles and returns the Pizza with the specified representation. This supports testing by using builders to create varied test objects or fixtures easily.[43]
Prototype
The Prototype pattern allows creating new objects by copying an existing instance (prototype), avoiding subclasses for every variant and reducing initialization costs. It involves a clone() method that returns a new instance.[39]
Language-agnostic pseudocode using shapes as prototypes:
// Abstract prototype
abstract class Shape {
protected int x;
protected int y;
protected String color;
public Shape() { }
public Shape(Shape source) {
this.x = source.x;
this.y = source.y;
this.color = source.color;
}
abstract Shape clone();
}
// Concrete prototype: Rectangle
class Rectangle extends Shape {
private int width;
private int height;
public Rectangle() { }
public Rectangle(Rectangle source) {
super(source);
this.width = source.width;
this.height = source.height;
}
public Shape clone() {
return new Rectangle(this);
}
}
// Concrete prototype: Circle
class Circle extends Shape {
private int radius;
public Circle() { }
public Circle(Circle source) {
super(source);
this.radius = source.radius;
}
public Shape clone() {
return new Circle(this);
}
}
// Abstract prototype
abstract class Shape {
protected int x;
protected int y;
protected String color;
public Shape() { }
public Shape(Shape source) {
this.x = source.x;
this.y = source.y;
this.color = source.color;
}
abstract Shape clone();
}
// Concrete prototype: Rectangle
class Rectangle extends Shape {
private int width;
private int height;
public Rectangle() { }
public Rectangle(Rectangle source) {
super(source);
this.width = source.width;
this.height = source.height;
}
public Shape clone() {
return new Rectangle(this);
}
}
// Concrete prototype: Circle
class Circle extends Shape {
private int radius;
public Circle() { }
public Circle(Circle source) {
super(source);
this.radius = source.radius;
}
public Shape clone() {
return new Circle(this);
}
}
Execution involves a client holding prototypes (e.g., a Rectangle with set properties); calling clone() invokes the constructor copying all fields from the source, producing an independent new instance; the client can then modify the clone without affecting the original. In testing, prototypes enable quick creation of similar test objects by cloning a base fixture and adjusting specifics.[39]