CrackedRuby CrackedRuby

Creational Patterns Overview

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