Creational Design Patterns

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.