3 min read
On this page

Creational Design Patterns

Design patterns catalog — Creational, Structural, and Behavioral categories

Creational patterns abstract the object creation process, making systems independent of how their objects are created, composed, and represented.

Singleton

Ensure a class has exactly one instance and provide a global access point.

CLASS Config
    FIELDS: db_url, max_connections

GLOBAL CONFIG ← uninitialized (lazy)

FUNCTION CONFIG()
    IF CONFIG is not initialized
        CONFIG ← new Config(
            db_url: ENV("DATABASE_URL"),
            max_connections: 10
        )
    RETURN CONFIG

When to use: Configuration, connection pools, loggers, hardware access.

Caution: Singletons are effectively global state. Make testing harder (can't inject mocks). Consider dependency injection instead.

Thread safety: Rust's OnceLock (or lazy_static!) handles thread-safe initialization. In other languages, use double-checked locking or language-specific mechanisms.

Factory Method

Define an interface for creating objects, but let subclasses/implementations decide which class to instantiate.

INTERFACE Transport
    PROCEDURE DELIVER(package)

CLASS Truck IMPLEMENTS Transport
    PROCEDURE DELIVER(package)  PRINT "Truck delivers: ", package

CLASS Ship IMPLEMENTS Transport
    PROCEDURE DELIVER(package)  PRINT "Ship delivers: ", package

// Factory function
FUNCTION CREATE_TRANSPORT(mode)
    IF mode = "land" THEN RETURN new Truck
    IF mode = "sea"  THEN RETURN new Ship
    ELSE ERROR "Unknown transport"

When to use: When the exact type to create depends on runtime conditions. When you want to decouple creation from usage.

Abstract Factory

Provide an interface for creating families of related objects without specifying concrete classes.

INTERFACE Button     PROCEDURE RENDER()
INTERFACE Checkbox   PROCEDURE RENDER()

INTERFACE UIFactory
    FUNCTION CREATE_BUTTON() -> Button
    FUNCTION CREATE_CHECKBOX() -> Checkbox

CLASS DarkThemeFactory IMPLEMENTS UIFactory
    FUNCTION CREATE_BUTTON()    RETURN new DarkButton
    FUNCTION CREATE_CHECKBOX()  RETURN new DarkCheckbox

CLASS LightThemeFactory IMPLEMENTS UIFactory
    FUNCTION CREATE_BUTTON()    RETURN new LightButton
    FUNCTION CREATE_CHECKBOX()  RETURN new LightCheckbox

PROCEDURE RENDER_UI(factory: UIFactory)
    btn ← factory.CREATE_BUTTON()
    chk ← factory.CREATE_CHECKBOX()
    btn.RENDER()
    chk.RENDER()

When to use: When you need to create families of related objects that must be used together. Platform-specific UI, database-specific DAOs.

Builder

Construct complex objects step by step. Separate construction from representation.

CLASS HttpRequest
    FIELDS: method, url, headers, body, timeout_ms

CLASS HttpRequestBuilder
    FIELDS: method, url, headers, body, timeout_ms

    CONSTRUCTOR NEW(method, url)
        self.method ← method
        self.url ← url
        self.headers ← empty list
        self.body ← NIL
        self.timeout_ms ← 30000

    FUNCTION HEADER(key, value) -> self
        APPEND (key, value) TO self.headers
        RETURN self

    FUNCTION BODY(body) -> self
        self.body ← body
        RETURN self

    FUNCTION TIMEOUT(ms) -> self
        self.timeout_ms ← ms
        RETURN self

    FUNCTION BUILD() -> HttpRequest
        RETURN new HttpRequest(self.method, self.url, self.headers,
                               self.body, self.timeout_ms)

// Usage — reads like a specification
request ← HttpRequestBuilder.NEW("POST", "https://api.example.com/users")
    .HEADER("Content-Type", "application/json")
    .HEADER("Authorization", "Bearer token123")
    .BODY('{"name": "Alice"}')
    .TIMEOUT(5000)
    .BUILD()

When to use: Objects with many optional parameters. Complex construction logic. Readable construction API.

Rust idiom: Very common. Used by reqwest, clap, serde_json::Builder, and many more.

Prototype

Create new objects by cloning existing ones. Useful when object creation is expensive and copies are cheaper.

CLASS Document (Cloneable)
    FIELDS: title, content, metadata
    // expensive to compute from scratch

    FUNCTION CREATE_VARIANT(new_title)
        doc ← CLONE(self)
        doc.title ← new_title
        RETURN doc

template ← new Document(...)
doc1 ← template.CREATE_VARIANT("Report Q1")
doc2 ← template.CREATE_VARIANT("Report Q2")

When to use: Creating objects is expensive (complex initialization, database queries). You need many similar objects. Runtime object creation from prototypes.

In Rust: The Clone trait is the standard mechanism.

Object Pool

Reuse objects from a pool instead of creating and destroying them repeatedly.

CLASS ConnectionPool
    FIELDS: connections (list), max_size

    FUNCTION ACQUIRE()
        IF connections is not empty
            RETURN POP(connections)
        RETURN NIL

    PROCEDURE RELEASE(conn)
        IF length(connections) < max_size
            APPEND conn TO connections
        // else: drop the connection

When to use: Object creation is expensive (database connections, thread creation, large allocations). Objects are used frequently and briefly.

Examples: Database connection pools (r2d2, deadpool), thread pools, buffer pools.

Lazy Initialization

Defer object creation until it's actually needed.

CLASS Service
    FIELDS: resource ← uninitialized (lazy)

    CONSTRUCTOR NEW()
        self.resource ← LAZY

    FUNCTION GET_RESOURCE()
        IF resource is not initialized
            resource ← new ExpensiveResource()
            // Expensive initialization — only runs once, on first access
        RETURN resource

When to use: Initialization is expensive and may not be needed. Reduce startup time.

Pattern Selection Guide

| Situation | Pattern | |---|---| | Exactly one instance needed | Singleton | | Choose implementation at runtime | Factory Method | | Create families of related objects | Abstract Factory | | Complex object with many options | Builder | | Copy existing objects cheaply | Prototype | | Reuse expensive objects | Object Pool | | Defer expensive initialization | Lazy Initialization |

Applications in CS

  • Frameworks: Dependency injection containers use Factory + Singleton patterns.
  • Configuration: Builder pattern for complex config objects. Singleton for global config.
  • Testing: Factory methods create test fixtures. Prototypes for test data.
  • Resource management: Connection pools, thread pools (object pool pattern).
  • Serialization: Builder pattern for constructing objects from serialized data.