Overview
Creational patterns address object creation mechanisms, controlling the instantiation process to make systems independent of how objects are created, composed, and represented. These patterns emerged from the Gang of Four design patterns catalog as a response to the complexity and rigidity of direct object instantiation using constructors and the new operator.
The five canonical creational patterns—Singleton, Factory Method, Abstract Factory, Builder, and Prototype—each solve specific problems related to object creation. Rather than creating objects directly, these patterns introduce an abstraction layer that encapsulates the creation logic, allowing for more flexible and maintainable code.
Creational patterns prove valuable when direct instantiation creates coupling between classes, when the system needs to work with families of related objects, when object creation involves complex initialization steps, or when the concrete types to instantiate should be determined at runtime rather than compile time. They shift the responsibility of instantiation away from the client code, promoting loose coupling and adherence to the Open/Closed Principle.
# Direct instantiation creates tight coupling
class ReportGenerator
def initialize
@database = PostgreSQLDatabase.new # Hard-coded dependency
end
end
# Factory pattern decouples creation
class ReportGenerator
def initialize(database_factory)
@database = database_factory.create # Flexible creation
end
end
The distinction between creational patterns and simple object instantiation lies in the level of abstraction and control. Direct instantiation exposes the concrete class to the client, creating compile-time dependencies. Creational patterns hide these details behind interfaces or abstract classes, allowing the system to vary the types of objects it creates without changing client code.
Key Principles
Creational patterns share several fundamental principles that guide their design and application. The primary principle involves separating object construction from object use, establishing a clear boundary between the code that creates objects and the code that uses them. This separation enables each part of the system to change independently.
Encapsulation of Knowledge: Creational patterns encapsulate knowledge about which concrete classes the system uses and how instances of these classes are created and composed. This encapsulation protects client code from changes in the instantiation process or the introduction of new concrete types.
Dependency Inversion: These patterns implement the Dependency Inversion Principle by making high-level modules depend on abstractions rather than concrete implementations. Clients depend on interfaces or abstract classes, not on specific concrete classes they instantiate.
# High-level module depends on abstraction
class NotificationService
def initialize(notification_factory)
@factory = notification_factory
end
def notify(user, message)
notification = @factory.create_for(user.preferences)
notification.send(user, message)
end
end
Control Over Instantiation: Creational patterns provide fine-grained control over the instantiation process. They can limit the number of instances (Singleton), defer instantiation decisions (Factory Method), ensure proper initialization sequences (Builder), or control instance reuse (Prototype). This control addresses scenarios where simple constructors prove insufficient.
Elimination of Hardcoded Class Names: By replacing direct instantiation with factory methods or builder interfaces, creational patterns eliminate hardcoded class names from client code. The client references only abstract types, making the system more flexible and easier to extend with new concrete types.
Complexity Management: Object creation often involves more than calling a constructor. It may require multi-step initialization, conditional logic based on context, coordination between related objects, or configuration from external sources. Creational patterns centralize this complexity in dedicated components rather than scattering it throughout client code.
The principle of abstraction applies differently across creational patterns. Factory patterns abstract the choice of which class to instantiate, Builder patterns abstract the construction process itself, Singleton patterns abstract instance management, and Prototype patterns abstract the cloning mechanism. Despite these differences, all creational patterns share the goal of making a system independent of how its objects are created.
Ruby Implementation
Ruby's dynamic nature and metaprogramming capabilities influence how creational patterns manifest in the language. Ruby provides language features that simplify or alter traditional pattern implementations seen in statically-typed languages.
Singleton Pattern in Ruby: Ruby includes a Singleton module in the standard library that implements the pattern using module mix-in functionality. This approach uses Ruby's module system to ensure single instance semantics.
require 'singleton'
class Configuration
include Singleton
attr_accessor :database_url, :api_key
def initialize
@database_url = ENV['DATABASE_URL']
@api_key = ENV['API_KEY']
end
end
# Access the single instance
config = Configuration.instance
config.database_url = 'postgres://localhost/myapp'
# Attempting to create new instance raises error
# Configuration.new # => NoMethodError: private method 'new'
The Singleton module makes the new method private and provides the instance class method. Thread-safety comes built-in, as the module handles synchronization internally. For simpler cases, Ruby developers often use module constants or class-level instance variables instead of full Singleton implementations.
Factory Method with Ruby Idioms: Ruby's dynamic typing and duck typing reduce the need for formal factory hierarchies. Factory methods often appear as simple class methods or module functions that return appropriate objects based on parameters.
class Document
def self.create(type, content)
case type
when :pdf
PDFDocument.new(content)
when :word
WordDocument.new(content)
when :html
HTMLDocument.new(content)
else
raise ArgumentError, "Unknown document type: #{type}"
end
end
end
# Usage
doc = Document.create(:pdf, "Report content")
More sophisticated factory implementations use hash lookups with proc values or maintain registries of creator objects. Ruby's const_get method enables string-to-class resolution, supporting plugin architectures where class names come from configuration.
class PluginFactory
@plugins = {}
def self.register(name, klass)
@plugins[name] = klass
end
def self.create(name, *args)
klass = @plugins[name] || Object.const_get(name)
klass.new(*args)
end
end
PluginFactory.register(:custom, CustomPlugin)
plugin = PluginFactory.create(:custom, config_options)
Builder Pattern with Method Chaining: Ruby's syntax makes builder patterns particularly expressive through method chaining. The builder returns self from each configuration method, allowing fluent interfaces.
class QueryBuilder
def initialize(table)
@table = table
@conditions = []
@order = nil
@limit = nil
end
def where(condition)
@conditions << condition
self
end
def order_by(column)
@order = column
self
end
def limit(count)
@limit = count
self
end
def build
query = "SELECT * FROM #{@table}"
query += " WHERE #{@conditions.join(' AND ')}" unless @conditions.empty?
query += " ORDER BY #{@order}" if @order
query += " LIMIT #{@limit}" if @limit
query
end
end
# Fluent interface
query = QueryBuilder.new('users')
.where('age > 18')
.where('active = true')
.order_by('created_at DESC')
.limit(10)
.build
Abstract Factory with Modules: Ruby uses modules to implement abstract factories, taking advantage of Ruby's ability to mix modules into classes dynamically and to use modules as namespaces.
module DatabaseFactory
def self.create_connection(type)
case type
when :postgresql
PostgreSQL::Connection
when :mysql
MySQL::Connection
when :sqlite
SQLite::Connection
end
end
def self.create_query_builder(type)
case type
when :postgresql
PostgreSQL::QueryBuilder
when :mysql
MySQL::QueryBuilder
when :sqlite
SQLite::QueryBuilder
end
end
end
# Creating family of related objects
conn = DatabaseFactory.create_connection(:postgresql).new(config)
builder = DatabaseFactory.create_query_builder(:postgresql).new
Prototype Pattern with Clone: Ruby's clone and dup methods provide built-in prototype functionality. These methods create shallow copies of objects, which proves sufficient for many use cases. For deep copying, Ruby offers Marshal.load(Marshal.dump(object)) or custom deep copy implementations.
class Configuration
attr_accessor :server, :port, :timeout, :retries
def initialize
@server = 'localhost'
@port = 8080
@timeout = 30
@retries = 3
end
def clone
copy = super
# Perform any necessary deep copying
copy
end
end
base_config = Configuration.new
dev_config = base_config.clone
dev_config.server = 'dev.example.com'
prod_config = base_config.clone
prod_config.server = 'prod.example.com'
prod_config.retries = 5
Ruby's flexibility allows developers to implement creational patterns with less boilerplate than statically-typed languages require. The language's features—blocks, procs, dynamic typing, metaprogramming, and module system—enable pattern implementations that feel natural and idiomatic rather than forced.
Common Patterns
Each creational pattern addresses specific object creation challenges and applies to different scenarios. Understanding the characteristics and appropriate use cases for each pattern enables effective pattern selection.
Singleton Pattern: Ensures a class has only one instance and provides a global access point to that instance. The pattern applies when exactly one object must coordinate actions across the system, such as configuration managers, logging services, or connection pools.
class Logger
include Singleton
def initialize
@log_file = File.open('application.log', 'a')
end
def log(message)
@log_file.puts "[#{Time.now}] #{message}"
@log_file.flush
end
def close
@log_file.close if @log_file && !@log_file.closed?
end
end
# Single instance accessed globally
Logger.instance.log("Application started")
Logger.instance.log("Processing request")
The Singleton pattern introduces global state, which can complicate testing and create hidden dependencies. Modern Ruby applications often prefer dependency injection over Singletons, passing shared instances explicitly rather than accessing them through global methods.
Factory Method Pattern: Defines an interface for creating objects but allows subclasses to decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses, promoting loose coupling between creator and product.
class PaymentProcessor
def process_payment(amount)
gateway = create_gateway
gateway.charge(amount)
end
def create_gateway
raise NotImplementedError, 'Subclasses must implement create_gateway'
end
end
class CreditCardProcessor < PaymentProcessor
def create_gateway
StripeGateway.new
end
end
class PayPalProcessor < PaymentProcessor
def create_gateway
PayPalGateway.new
end
end
class CryptocurrencyProcessor < PaymentProcessor
def create_gateway
CoinbaseGateway.new
end
end
This pattern proves valuable when a class cannot anticipate the type of objects it must create, when a class wants its subclasses to specify the objects it creates, or when classes delegate responsibility to helper subclasses and need to localize knowledge about which helper subclass is the delegate.
Abstract Factory Pattern: Provides an interface for creating families of related or dependent objects without specifying their concrete classes. The pattern applies when the system must be independent of how its products are created and when families of related products must be used together.
class UIFactory
def create_button
raise NotImplementedError
end
def create_checkbox
raise NotImplementedError
end
def create_textfield
raise NotImplementedError
end
end
class WindowsUIFactory < UIFactory
def create_button
WindowsButton.new
end
def create_checkbox
WindowsCheckbox.new
end
def create_textfield
WindowsTextField.new
end
end
class MacUIFactory < UIFactory
def create_button
MacButton.new
end
def create_checkbox
MacCheckbox.new
end
def create_textfield
MacTextField.new
end
end
# Client code works with factories
class Application
def initialize(ui_factory)
@factory = ui_factory
end
def create_ui
button = @factory.create_button
checkbox = @factory.create_checkbox
textfield = @factory.create_textfield
[button, checkbox, textfield]
end
end
Abstract Factory ensures that products from the same family work together correctly. The pattern isolates concrete classes and makes exchanging product families easy but makes supporting new kinds of products difficult, as the interface fixes the set of products that can be created.
Builder Pattern: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations. The pattern applies when the algorithm for creating a complex object should be independent of the parts that make up the object and how they are assembled.
class HTTPRequestBuilder
def initialize
@request = HTTPRequest.new
end
def method(verb)
@request.method = verb
self
end
def url(path)
@request.url = path
self
end
def header(key, value)
@request.headers[key] = value
self
end
def body(content)
@request.body = content
self
end
def query_param(key, value)
@request.query_params[key] = value
self
end
def build
@request.validate!
@request
end
end
# Step-by-step construction
request = HTTPRequestBuilder.new
.method(:post)
.url('/api/users')
.header('Content-Type', 'application/json')
.header('Authorization', 'Bearer token123')
.body('{"name":"John","email":"john@example.com"}')
.query_param('version', 'v2')
.build
Builder separates construction logic from representation, provides control over the construction process, and permits different implementations of the builder interface to create different representations of the product. The pattern works well when object creation involves many optional parameters or complex validation rules.
Prototype Pattern: Specifies the kinds of objects to create using a prototypical instance and creates new objects by copying this prototype. The pattern applies when the classes to instantiate are specified at runtime or when creating an instance is expensive compared to copying.
class DocumentTemplate
attr_accessor :header, :footer, :styles, :sections
def initialize
@header = {}
@footer = {}
@styles = {}
@sections = []
end
def clone
copy = super
copy.header = @header.dup
copy.footer = @footer.dup
copy.styles = @styles.dup
copy.sections = @sections.map(&:dup)
copy
end
end
# Create base template with expensive setup
base_template = DocumentTemplate.new
base_template.styles = load_styles_from_file('corporate.css')
base_template.header = { logo: 'company_logo.png', title: 'Report' }
# Clone for specific documents
quarterly_report = base_template.clone
quarterly_report.sections = ['Q1 Results', 'Q2 Results', 'Q3 Results', 'Q4 Results']
annual_review = base_template.clone
annual_review.sections = ['Executive Summary', 'Financial Performance', 'Future Outlook']
Prototype reduces subclassing requirements, hides concrete product classes from clients, and allows adding and removing products at runtime. The main challenge involves implementing the clone operation correctly, particularly for objects with complex internal structure or circular references.
Design Considerations
Selecting the appropriate creational pattern requires analyzing the specific object creation requirements and understanding the trade-offs each pattern introduces. The decision involves examining coupling, flexibility, complexity, and future extensibility needs.
When to Use Singleton: Choose Singleton when exactly one instance of a class must exist and that instance must be accessible from a well-known access point. Configuration objects, thread pools, database connection pools, and logging systems commonly use this pattern. However, Singletons introduce global state and can complicate testing because tests cannot easily replace the singleton instance with a mock or stub. Consider dependency injection as an alternative when testability matters.
When to Use Factory Method: Apply Factory Method when a class cannot anticipate the class of objects it must create, when a class wants its subclasses to specify the objects it creates, or when classes delegate responsibility to one of several helper subclasses. The pattern proves valuable in frameworks where the framework code needs to work with application-specific classes. Factory Method promotes loose coupling by eliminating direct dependencies on concrete classes.
The pattern introduces complexity through additional classes and inheritance hierarchies. Consider simple factory functions (not the full pattern) when inheritance hierarchy proves unnecessary. In Ruby, class methods or module functions often suffice without requiring full abstract factory classes.
When to Use Abstract Factory: Abstract Factory suits situations where the system must be independent of how its products are created, composed, and represented, and where the system must be configured with one of multiple families of products. The pattern works well for cross-platform applications, theme systems, and database abstraction layers where different implementations must be swapped wholesale.
Abstract Factory increases isolation between concrete classes and makes exchanging product families straightforward. However, supporting new kinds of products requires changing the abstract factory interface and all of its subclasses. This limitation makes Abstract Factory less suitable when the set of products changes frequently.
When to Use Builder: Builder applies when constructing complex objects involves many steps, when object creation requires different representations, or when objects have many optional components. The pattern shines in scenarios with numerous configuration options, such as HTTP request builders, query builders, or complex object initialization with validation rules.
# Builder handles complex validation and optional parameters
class ServerConfigBuilder
def initialize
@config = ServerConfig.new
@config.port = 8080 # Default
@config.threads = 5 # Default
end
def port(number)
raise ArgumentError, "Port must be between 1 and 65535" unless (1..65535).include?(number)
@config.port = number
self
end
def threads(count)
raise ArgumentError, "Thread count must be positive" unless count > 0
@config.threads = count
self
end
def ssl(cert_path, key_path)
raise ArgumentError, "Certificate file not found" unless File.exist?(cert_path)
raise ArgumentError, "Key file not found" unless File.exist?(key_path)
@config.ssl_cert = cert_path
@config.ssl_key = key_path
self
end
def build
@config.validate!
@config
end
end
Builder provides better control over the construction process than telescoping constructors (constructors with many parameters). The pattern separates construction logic from business logic and makes the construction steps explicit. Consider Builder when object construction involves dependencies between components or when validation must occur during construction.
When to Use Prototype: Prototype suits scenarios where object creation costs more than copying existing objects, where objects have slight variations but share core configuration, or where the system must be independent of how its products are created. The pattern works well for document templates, game entity spawning, and configuration management.
Prototype reduces the need for parallel class hierarchies of factories and products. The pattern hides concrete product classes from clients and allows adding and removing products at runtime by registering new prototypes. The main challenge involves correctly implementing the clone operation, particularly ensuring deep copies when necessary.
Trade-off Analysis: Creational patterns trade simplicity for flexibility. Direct instantiation proves simpler but creates rigid coupling. Creational patterns introduce additional classes and indirection but provide flexibility to vary object creation. The complexity cost justifies itself when:
- The system needs to support multiple implementations that may be selected at runtime
- Object creation involves complex initialization that should be encapsulated
- The set of concrete classes may grow over time
- Testing requires substituting mock implementations
- The system must work with families of related objects
Consider the maintenance burden of each pattern. Singleton creates implicit global state. Factory hierarchies require changes to multiple classes when adding new products. Abstract Factory complicates adding new product types. Builder requires maintaining the builder interface alongside the product class. Prototype requires careful implementation of cloning semantics.
Practical Examples
Creational patterns solve real problems in software systems. These examples demonstrate patterns applied to concrete scenarios with complete context and implementation details.
Example 1: Database Connection Factory: A reporting system needs to generate reports from different database types—PostgreSQL, MySQL, and SQLite. The system should not couple report generation logic to specific database implementations.
class DatabaseConnection
def execute_query(sql)
raise NotImplementedError
end
def close
raise NotImplementedError
end
end
class PostgreSQLConnection < DatabaseConnection
def initialize(config)
@conn = PG.connect(
host: config[:host],
port: config[:port],
dbname: config[:database],
user: config[:user],
password: config[:password]
)
end
def execute_query(sql)
@conn.exec(sql)
end
def close
@conn.close
end
end
class MySQLConnection < DatabaseConnection
def initialize(config)
@conn = Mysql2::Client.new(
host: config[:host],
port: config[:port],
database: config[:database],
username: config[:user],
password: config[:password]
)
end
def execute_query(sql)
@conn.query(sql)
end
def close
@conn.close
end
end
class DatabaseFactory
def self.create_connection(type, config)
case type
when :postgresql
PostgreSQLConnection.new(config)
when :mysql
MySQLConnection.new(config)
when :sqlite
SQLiteConnection.new(config)
else
raise ArgumentError, "Unsupported database type: #{type}"
end
end
end
# Report generator remains independent of database type
class ReportGenerator
def initialize(db_type, db_config)
@connection = DatabaseFactory.create_connection(db_type, db_config)
end
def generate_sales_report(start_date, end_date)
sql = "SELECT * FROM sales WHERE date BETWEEN '#{start_date}' AND '#{end_date}'"
results = @connection.execute_query(sql)
format_results(results)
ensure
@connection.close
end
end
This implementation uses Factory Method to decouple report generation from specific database implementations. Adding support for new database types requires only implementing a new connection class and updating the factory, without modifying report generation logic.
Example 2: HTTP Client Builder: An API client needs to construct HTTP requests with various combinations of headers, query parameters, request bodies, authentication, timeouts, and retry logic. Builder pattern provides a fluent interface for request construction.
class HTTPRequest
attr_accessor :method, :url, :headers, :body, :query_params, :timeout, :retries
def initialize
@headers = {}
@query_params = {}
@timeout = 30
@retries = 0
end
def execute
# Execute the request using the configured parameters
uri = URI(@url)
uri.query = URI.encode_www_form(@query_params) unless @query_params.empty?
# Execute with configured retries and timeout
attempt = 0
begin
# Actual HTTP request execution logic here
rescue StandardError => e
attempt += 1
retry if attempt <= @retries
raise
end
end
end
class HTTPRequestBuilder
def initialize(base_url)
@base_url = base_url
@request = HTTPRequest.new
end
def get(path)
@request.method = :get
@request.url = "#{@base_url}#{path}"
self
end
def post(path)
@request.method = :post
@request.url = "#{@base_url}#{path}"
self
end
def with_json_body(data)
@request.headers['Content-Type'] = 'application/json'
@request.body = JSON.generate(data)
self
end
def with_auth_token(token)
@request.headers['Authorization'] = "Bearer #{token}"
self
end
def with_query(params)
@request.query_params.merge!(params)
self
end
def timeout(seconds)
@request.timeout = seconds
self
end
def retries(count)
@request.retries = count
self
end
def build
@request
end
end
# Usage: Building different types of requests
client = HTTPRequestBuilder.new('https://api.example.com')
# Simple GET request
users_request = client
.get('/users')
.with_auth_token('abc123')
.build
# Complex POST request with retries
create_request = client
.post('/users')
.with_auth_token('abc123')
.with_json_body({ name: 'John', email: 'john@example.com' })
.with_query({ version: 'v2' })
.timeout(60)
.retries(3)
.build
The Builder pattern allows constructing requests incrementally, handles validation at build time, and provides a readable API for complex request configuration. The fluent interface makes the construction process explicit and self-documenting.
Example 3: Document Template Prototype: A document generation system creates various reports from base templates. Creating templates from scratch involves expensive operations like loading styles, validating structure, and parsing layout specifications. Prototype pattern allows cloning configured templates.
class DocumentTemplate
attr_accessor :styles, :layout, :header, :footer, :metadata
def initialize
@styles = {}
@layout = {}
@header = nil
@footer = nil
@metadata = {}
end
def load_styles(stylesheet_path)
# Expensive operation: parse and validate CSS
@styles = CSSParser.parse(File.read(stylesheet_path))
end
def load_layout(layout_path)
# Expensive operation: parse layout specification
@layout = LayoutParser.parse(File.read(layout_path))
end
def clone
copy = super
# Deep copy mutable attributes
copy.styles = @styles.dup
copy.layout = @layout.dup
copy.metadata = @metadata.dup
copy
end
def render(content)
# Use template configuration to render content
renderer = DocumentRenderer.new(@styles, @layout)
renderer.add_header(@header) if @header
renderer.add_footer(@footer) if @footer
renderer.render(content)
end
end
# Create base template once (expensive)
corporate_template = DocumentTemplate.new
corporate_template.load_styles('corporate_style.css')
corporate_template.load_layout('standard_layout.xml')
corporate_template.header = { logo: 'logo.png', title: 'Corporate Report' }
corporate_template.footer = { text: 'Confidential', page_numbers: true }
# Clone for different reports (cheap)
quarterly_report = corporate_template.clone
quarterly_report.metadata = { type: 'quarterly', period: 'Q4 2024' }
quarterly_report.render(quarterly_data)
annual_report = corporate_template.clone
annual_report.metadata = { type: 'annual', year: '2024' }
annual_report.render(annual_data)
executive_summary = corporate_template.clone
executive_summary.header[:title] = 'Executive Summary'
executive_summary.render(summary_data)
This example demonstrates Prototype pattern avoiding repeated expensive initialization operations. Each clone operation copies the configured template quickly, allowing the system to generate many documents efficiently from a small set of base templates.
Reference
Pattern Comparison Matrix
| Pattern | Purpose | Instance Control | Primary Use Case |
|---|---|---|---|
| Singleton | Single shared instance | Enforces one instance | Global configuration, logging |
| Factory Method | Subclass decides type | Creates one object | Framework extension points |
| Abstract Factory | Family of related objects | Creates multiple related objects | Cross-platform systems, themes |
| Builder | Step-by-step construction | Creates complex objects | Objects with many optional parameters |
| Prototype | Clone existing objects | Copies configured instances | Template-based creation |
Pattern Selection Guide
| Scenario | Recommended Pattern | Rationale |
|---|---|---|
| Need exactly one instance globally | Singleton | Guarantees single instance access |
| Subclasses determine concrete types | Factory Method | Delegates instantiation to subclasses |
| Multiple product families must work together | Abstract Factory | Ensures product family consistency |
| Object has many optional parameters | Builder | Handles complex initialization clearly |
| Object creation expensive, configuration varies | Prototype | Amortizes creation cost through cloning |
| Runtime type determination needed | Factory Method or Abstract Factory | Defers instantiation decisions |
| Complex validation during construction | Builder | Validates incrementally during build |
Ruby Implementation Patterns
| Pattern | Ruby Approach | Key Ruby Features Used |
|---|---|---|
| Singleton | Include Singleton module | Module mix-in, class-level state |
| Factory Method | Class methods or case statements | Dynamic typing, const_get |
| Abstract Factory | Module-based factories | Modules as namespaces |
| Builder | Method chaining with self | Method chaining, fluent interfaces |
| Prototype | Clone or dup methods | Built-in object copying |
Common Implementation Methods
| Pattern | Key Methods | Purpose |
|---|---|---|
| Singleton | instance | Returns single instance |
| Singleton | private_class_method :new | Prevents direct instantiation |
| Factory Method | create, build, make | Factory method names |
| Builder | build, finalize | Finalizes construction |
| Builder | Configuration methods returning self | Enables method chaining |
| Prototype | clone, dup | Creates object copies |
| Prototype | initialize_copy | Customizes cloning behavior |
Pattern Drawbacks
| Pattern | Drawback | Mitigation Strategy |
|---|---|---|
| Singleton | Global state complicates testing | Use dependency injection instead |
| Singleton | Hidden dependencies | Make dependencies explicit |
| Factory Method | Proliferation of subclasses | Consider simpler factory functions |
| Abstract Factory | Difficult to add new product types | Design stable product families |
| Builder | Parallel class hierarchy | Share builder implementation when possible |
| Prototype | Clone implementation complexity | Use built-in clone/dup when sufficient |
When Patterns Combine
| Combination | Use Case | Example |
|---|---|---|
| Singleton + Factory Method | Factory that is also singleton | Database connection factory |
| Builder + Factory Method | Builder creates product via factory | Complex object with variant components |
| Prototype + Factory Method | Factory returns cloned prototypes | Object pool with prototype templates |
| Abstract Factory + Singleton | Single factory for product family | UI component factory per platform |