---
# System prepended metadata

title: Creational Design Patterns

---

# Creational Design Patterns

## Definition

Creational design patterns are a category of object-oriented design patterns that ==***abstract and encapsulate the process of object creation***==. Rather than instantiating objects directly using the `new` keyword (or its language equivalent), creational patterns provide controlled, flexible, and reusable mechanisms for constructing objects.

In modern software systems, object creation is rarely as simple as calling a constructor. Real-world systems must decide:

- Which concrete class to instantiate at runtime
- How to configure complex objects with many dependencies
- How to ensure only one instance exists across an entire application
- How to construct families of related objects
- How to clone or replicate existing object state efficiently

Creational patterns address these concerns by separating the *what* (the object being created) from the *how* (the creation logic). This separation is foundational to building loosely coupled, testable, and maintainable software systems.

## Significance

In small programs, calling `new SomeClass()` works fine. In large-scale systems, however, direct instantiation creates several severe problems:

- **Tight coupling**: Code that instantiates concrete classes directly cannot easily switch implementations without modification.
- **Difficulty in testing**: Hard-coded dependencies cannot be replaced with mocks or stubs.
- **Configuration complexity**: Production, staging, and test environments often need different object configurations.
- **Resource management issues**: Some objects (database connections, configuration managers, loggers) must be shared, not duplicated.
- **Construction complexity**: Objects with many optional parameters lead to unreadable constructors.

Creational patterns are widely used in:

- Enterprise application frameworks (Spring, .NET, Hibernate)
- Game engines (entity factories, prefab cloning)
- Cloud infrastructure SDKs (AWS SDK builders, Kubernetes client factories)
- ORM libraries (entity instantiation, lazy loading)
- Dependency injection containers (Spring, Guice, Dagger, ASP.NET Core DI)

## Intuition

Think of an architect designing a large residential complex. The architect does not personally lay every brick. Instead, the architect designs blueprints, hires specialists, and delegates construction to teams. Each team knows how to build a particular component, kitchens, plumbing, electrical systems, without the architect micromanaging every detail.

Creational patterns work the same way. The code that *uses* an object should not be burdened with the knowledge of *how* to build that object. Construction logic is delegated to specialized constructs factories, builders, prototypes, or injection containers that hide complexity behind a clean interface.

Another useful mental model: object creation is like ordering food at a restaurant. You do not enter the kitchen and prepare the meal yourself. You declare what you want, and the kitchen handles ingredients, cooking, and plating. Creational patterns are the restaurant kitchens of software construction.



## Core Concepts and Principles

Creational patterns are governed by several object-oriented design principles:

**Encapsulation of creation logic** — The details of object construction should be hidden from the client code that uses the object. The client should depend on abstractions, not concrete implementations.

**Program to an interface, not an implementation** — Clients should hold references to interfaces or abstract base classes. The actual concrete type is chosen by the creational mechanism, allowing future substitution.

**Single Responsibility Principle (SRP)** — Object creation is a responsibility distinct from object behavior. Mixing the two violates SRP and produces fragile code.

**Open/Closed Principle (OCP)** — Creational patterns make it possible to introduce new object types without modifying existing client code.

**Dependency Inversion Principle (DIP)** — High-level modules should not depend on low-level modules; both should depend on abstractions. Creational patterns enforce this discipline.

**Common trade-offs** include:

- Added indirection and complexity versus flexibility gained
- Increased number of classes versus cleaner separation of concerns
- Runtime configuration cost versus static instantiation speed
- Hidden construction logic versus explicit, easy-to-read code

**Common mistakes** include:

- Overusing patterns where simple instantiation would suffice
- Using Singleton as a global variable, polluting the global state
- Creating deeply nested factory hierarchies that obscure intent
- Hard-coding concrete factories instead of injecting them



## Architecture Components

While creational patterns operate at the class level rather than the system level, they manifest in larger architectures through several recurring components.

**Client** — The code that needs a constructed object. It interacts only with abstractions and creational interfaces, never with concrete construction logic.

**Product** — The object being created. Often an interface or abstract class with multiple concrete implementations.

**Creator (Factory)** — The component responsible for producing Products. It encapsulates the decision of which concrete class to instantiate.

**Builder** — A specialized creator used when construction involves many steps or optional parameters.

**Prototype Registry** — A store of pre-configured objects that can be cloned on demand.

**Dependency Injection Container** — An infrastructure-level component that resolves and injects dependencies automatically based on configuration metadata.

**Configuration Source** — External files (YAML, JSON, environment variables) or annotations that drive what objects get created and how.

**Lifecycle Manager** — Manages object lifetimes: singleton, transient, scoped (per request, per session).



## Workflow and Request Lifecycle

Consider a typical web request in a modern backend service that uses creational patterns extensively:

1. A request enters the application server.
2. The framework's DI container resolves the controller, instantiating it with its required services (repositories, validators, mappers).
3. Each service may itself depend on further objects — database connections, HTTP clients, caches — which the container constructs or retrieves from a singleton registry.
4. Inside business logic, complex domain objects are built using Builder patterns for clarity.
5. When the system needs a polymorphic component (e.g., a payment gateway), a Factory Method selects the correct implementation based on configuration.
6. Some heavyweight objects are cloned from prototypes rather than constructed from scratch.
7. After the request completes, transient objects are garbage collected; singletons persist.

This workflow shows that creational patterns are not isolated curiosities — they are woven into the request lifecycle of nearly every non-trivial backend system.



## Algorithms, Design Techniques, and Patterns

This section presents each of the six creational patterns in depth.

### Singleton

**Definition**: Ensures a class has only one instance and provides a global access point to it.

**Intuition**: There is only one operating system kernel, one logging facility, one configuration registry. Some resources are inherently single-instance, and constructing them multiple times causes bugs or wastes resources.

**Structure**:

```mermaid
classDiagram
    class Singleton {
        -static instance: Singleton
        -Singleton()
        +static getInstance() Singleton
        +businessMethod()
    }
    Singleton --> Singleton : returns
```

**Implementation in Java (thread-safe with double-checked locking)**:

```java
public final class ConfigurationManager {
    private static volatile ConfigurationManager instance;
    private final Properties props;

    private ConfigurationManager() {
        props = loadConfiguration();
    }

    public static ConfigurationManager getInstance() {
        if (instance == null) {
            synchronized (ConfigurationManager.class) {
                if (instance == null) {
                    instance = new ConfigurationManager();
                }
            }
        }
        return instance;
    }

    public String get(String key) {
        return props.getProperty(key);
    }

    private Properties loadConfiguration() {
        // Load from file, env vars, etc.
        return new Properties();
    }
}
```

**Implementation in Python (using metaclass)**:

```python
class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class Logger(metaclass=SingletonMeta):
    def __init__(self):
        self.history = []

    def log(self, message: str) -> None:
        self.history.append(message)
        print(f"[LOG] {message}")
```

**Implementation in C++ (Meyers Singleton, thread-safe since C++11)**:

```cpp
class DatabaseConnection {
public:
    static DatabaseConnection& getInstance() {
        static DatabaseConnection instance;
        return instance;
    }

    DatabaseConnection(const DatabaseConnection&) = delete;
    DatabaseConnection& operator=(const DatabaseConnection&) = delete;

    void executeQuery(const std::string& sql) { /* ... */ }

private:
    DatabaseConnection() { /* connect */ }
};
```

**Use cases**: Logging frameworks, configuration managers, connection pools, hardware interface abstractions, caches, registries.

**Trade-offs**:

- Introduces hidden global state — a major source of testing difficulty.
- Tightly couples consumers to a specific class, violating DIP.
- Concurrency is non-trivial; naive implementations have race conditions.
- Modern practice often replaces Singleton with a DI-managed singleton-scoped service.

Singleton is the most overused and most misused creational pattern. In modern systems, prefer dependency-injected singleton-scoped services over hand-rolled Singletons.

### Factory Method

**Definition**: Defines an interface for creating an object but lets subclasses decide which concrete class to instantiate.

**Intuition**: A logistics company has many transport modes — trucks, ships, planes. The general workflow of "deliver a package" is the same, but the *vehicle* used depends on context. The Factory Method delegates the choice of vehicle to specialized subclasses.

**Structure**:

```mermaid
classDiagram
    class Creator {
        +factoryMethod()* Product
        +operation()
    }
    class ConcreteCreatorA {
        +factoryMethod() Product
    }
    class ConcreteCreatorB {
        +factoryMethod() Product
    }
    class Product {
        <<interface>>
    }
    class ConcreteProductA
    class ConcreteProductB

    Creator <|-- ConcreteCreatorA
    Creator <|-- ConcreteCreatorB
    Product <|.. ConcreteProductA
    Product <|.. ConcreteProductB
    ConcreteCreatorA ..> ConcreteProductA
    ConcreteCreatorB ..> ConcreteProductB
```

**Implementation in Java**:

```java
public interface Notification {
    void send(String message);
}

public class EmailNotification implements Notification {
    public void send(String message) { /* SMTP */ }
}

public class SMSNotification implements Notification {
    public void send(String message) { /* Twilio API */ }
}

public abstract class NotificationCreator {
    public abstract Notification createNotification();

    public void notifyUser(String message) {
        Notification n = createNotification();
        n.send(message);
    }
}

public class EmailCreator extends NotificationCreator {
    public Notification createNotification() {
        return new EmailNotification();
    }
}

public class SMSCreator extends NotificationCreator {
    public Notification createNotification() {
        return new SMSNotification();
    }
}
```

**Use cases**: Plugin systems, document parsers, payment processors, notification dispatchers, database driver selection.

**Trade-offs**:

- Adds a parallel hierarchy of creator classes.
- Improves extensibility — new product types require only a new subclass.
- Especially useful when the creation logic is non-trivial and varies by context.

### Abstract Factory

**Definition**: Provides an interface for creating *families* of related or dependent objects without specifying their concrete classes.

**Intuition**: Imagine designing a cross-platform UI library. On Windows, you need Windows-style buttons, scrollbars, and dialogs. On macOS, you need macOS-style equivalents. Abstract Factory ensures that whichever platform you target, you get a consistent family of widgets — never a Windows button accidentally placed in a macOS dialog.

**Structure**:

```mermaid
classDiagram
    class AbstractFactory {
        <<interface>>
        +createButton() Button
        +createCheckbox() Checkbox
    }
    class WindowsFactory {
        +createButton() WindowsButton
        +createCheckbox() WindowsCheckbox
    }
    class MacFactory {
        +createButton() MacButton
        +createCheckbox() MacCheckbox
    }
    class Button {
        <<interface>>
    }
    class Checkbox {
        <<interface>>
    }

    AbstractFactory <|.. WindowsFactory
    AbstractFactory <|.. MacFactory
    Button <|.. WindowsButton
    Button <|.. MacButton
    Checkbox <|.. WindowsCheckbox
    Checkbox <|.. MacCheckbox
```

**Implementation in Python**:

```python
from abc import ABC, abstractmethod


class Button(ABC):
    @abstractmethod
    def render(self): pass


class Checkbox(ABC):
    @abstractmethod
    def render(self): pass


class WindowsButton(Button):
    def render(self): print("Rendering Windows button")


class WindowsCheckbox(Checkbox):
    def render(self): print("Rendering Windows checkbox")


class MacButton(Button):
    def render(self): print("Rendering Mac button")


class MacCheckbox(Checkbox):
    def render(self): print("Rendering Mac checkbox")


class GUIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: pass

    @abstractmethod
    def create_checkbox(self) -> Checkbox: pass


class WindowsFactory(GUIFactory):
    def create_button(self): return WindowsButton()
    def create_checkbox(self): return WindowsCheckbox()


class MacFactory(GUIFactory):
    def create_button(self): return MacButton()
    def create_checkbox(self): return MacCheckbox()


def render_ui(factory: GUIFactory):
    factory.create_button().render()
    factory.create_checkbox().render()
```

**Use cases**: Cross-platform UI toolkits, multi-cloud SDKs (AWS vs Azure resource builders), themed component libraries, database driver families.

**Trade-offs**:

- Strongly enforces consistency across product families.
- Adding a new *product type* (e.g., a Slider) requires updating every factory — violates OCP in that dimension.
- Adding a new *family* (e.g., Linux UI) is straightforward.

### Builder

**Definition**: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.

**Intuition**: Constructing a deeply customizable object — like a SQL query, an HTTP request, or a meal at a build-your-own restaurant — using a single constructor with 15 parameters is painful and error-prone. The Builder pattern offers a fluent, step-by-step API.

**Structure**:

```mermaid
classDiagram
    class Director {
        -builder: Builder
        +construct()
    }
    class Builder {
        <<interface>>
        +reset()
        +setPartA()
        +setPartB()
        +getResult() Product
    }
    class ConcreteBuilder {
        -product: Product
        +reset()
        +setPartA()
        +setPartB()
        +getResult() Product
    }
    class Product

    Director o--> Builder
    Builder <|.. ConcreteBuilder
    ConcreteBuilder ..> Product
```

**Implementation in Java (fluent builder)**:

```java
public final class HttpRequest {
    private final String url;
    private final String method;
    private final Map<String, String> headers;
    private final String body;
    private final int timeoutMs;

    private HttpRequest(Builder b) {
        this.url = b.url;
        this.method = b.method;
        this.headers = b.headers;
        this.body = b.body;
        this.timeoutMs = b.timeoutMs;
    }

    public static Builder newBuilder() {
        return new Builder();
    }

    public static final class Builder {
        private String url;
        private String method = "GET";
        private Map<String, String> headers = new HashMap<>();
        private String body = "";
        private int timeoutMs = 30000;

        public Builder url(String url) { this.url = url; return this; }
        public Builder method(String m) { this.method = m; return this; }
        public Builder header(String k, String v) { headers.put(k, v); return this; }
        public Builder body(String b) { this.body = b; return this; }
        public Builder timeout(int ms) { this.timeoutMs = ms; return this; }

        public HttpRequest build() {
            if (url == null) throw new IllegalStateException("URL required");
            return new HttpRequest(this);
        }
    }
}

// Usage
HttpRequest req = HttpRequest.newBuilder()
    .url("https://api.example.com/users")
    .method("POST")
    .header("Content-Type", "application/json")
    .body("{\"name\":\"Alice\"}")
    .timeout(5000)
    .build();
```

**Use cases**: HTTP clients (OkHttp, Apache HttpClient), SQL query builders (jOOQ, QueryDSL), test data builders, configuration objects, AWS SDK request builders.

**Trade-offs**:

- Excellent readability for objects with many fields.
- Enforces immutability of the built product.
- Adds boilerplate; mitigated by language features (Lombok `@Builder`, Kotlin data classes).

!!! tip
    When a constructor has more than four parameters, especially with several optional ones, the Builder pattern almost always improves code clarity.

### Prototype

**Definition**: Creates new objects by cloning an existing object (the prototype) rather than instantiating from scratch.

**Intuition**: Imagine a game engine spawning hundreds of enemies. Building each enemy from raw configuration files is slow. Instead, you build one "template" enemy, then clone it. Cloning is cheap; full construction is expensive.

**Structure**:

```mermaid
classDiagram
    class Prototype {
        <<interface>>
        +clone() Prototype
    }
    class ConcretePrototype {
        -field1
        -field2
        +clone() ConcretePrototype
    }
    class Client {
        +operation()
    }
    Prototype <|.. ConcretePrototype
    Client --> Prototype
```

**Implementation in Python**:

```python
import copy
from dataclasses import dataclass, field


@dataclass
class Enemy:
    name: str
    health: int
    weapons: list = field(default_factory=list)
    abilities: dict = field(default_factory=dict)

    def clone(self) -> "Enemy":
        return copy.deepcopy(self)


# Pre-configure a prototype
goblin_template = Enemy(
    name="Goblin",
    health=50,
    weapons=["dagger"],
    abilities={"stealth": 0.7}
)

# Spawn many cheaply
swarm = [goblin_template.clone() for _ in range(100)]
swarm[0].name = "Goblin Captain"
swarm[0].health = 80
```

**Use cases**: Game development, document editors (duplicating elements), configuration templating, machine learning experiment templates, infrastructure-as-code resource templating.

**Trade-offs**:

- Cloning complex objects with circular references or non-serializable fields is tricky.
- Shallow vs deep copy must be chosen deliberately.
- Far faster than reconstructing from scratch for heavyweight objects.

### Dependency Injection

**Definition**: A design technique in which an object receives its dependencies from an external source rather than constructing them itself. While sometimes treated as a principle rather than a "pattern," Dependency Injection (DI) is the dominant creational mechanism in modern enterprise software.

**Intuition**: Instead of a class going out and finding its collaborators ("I need a database — let me create one"), the collaborators are handed to it ("Here is the database you need"). This inversion of control makes systems testable, configurable, and modular.

There are three primary forms of injection:

- **Constructor injection** (preferred): dependencies passed via the constructor.
- **Setter injection**: dependencies set via public setter methods after construction.
- **Interface injection**: the class implements an interface declaring its dependencies.

**Structure**:

```mermaid
flowchart LR
    Container[DI Container] -->|injects| Service[UserService]
    Container -->|injects| Repo[UserRepository]
    Service -->|depends on| Repo
    Config[Configuration / Annotations] --> Container
```

**Implementation in Java with Spring**:

```java
public interface UserRepository {
    User findById(long id);
}

@Repository
public class JpaUserRepository implements UserRepository {
    public User findById(long id) { /* ... */ return null; }
}

@Service
public class UserService {
    private final UserRepository repository;

    @Autowired
    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public User getUser(long id) {
        return repository.findById(id);
    }
}
```

**Implementation in Python (manual injection)**:

```python
from typing import Protocol


class UserRepository(Protocol):
    def find_by_id(self, user_id: int) -> dict: ...


class PostgresUserRepository:
    def find_by_id(self, user_id: int) -> dict:
        return {"id": user_id, "name": "Alice"}


class UserService:
    def __init__(self, repository: UserRepository):
        self._repo = repository

    def get_user(self, user_id: int) -> dict:
        return self._repo.find_by_id(user_id)


# Composition root
repo = PostgresUserRepository()
service = UserService(repo)
```

**Use cases**: Every modern backend framework — Spring (Java), ASP.NET Core (C#), NestJS (TypeScript), FastAPI (Python with `Depends`), Dagger and Hilt (Android), Angular (frontend).

**Trade-offs**:

- Massively improves testability — dependencies can be mocked or stubbed.
- Decouples object construction from object behavior.
- Container-based DI can obscure the dependency graph; debugging requires understanding the container.
- Misuse leads to "service locator" anti-pattern or over-injection.

!!! note
    Dependency Injection is the foundation of most modern enterprise systems. Mastering it is essential for backend engineering interviews and real-world architecture work.



## Scalability Considerations

Creational patterns themselves are class-level constructs, but they affect system-level scalability in important ways.

**Singleton and concurrency** — Singletons in multi-threaded environments must be implemented carefully. Lock contention on a shared instance can become a bottleneck. Stateless singletons scale well; stateful singletons may need synchronization or thread-local storage.

**Object pooling vs prototype** — When object construction is expensive (e.g., database connections), pooling pre-built objects is often more scalable than constructing on demand. Prototype patterns can support pooling.

**DI container performance** — In high-throughput systems, the cost of resolving dependencies on each request matters. Most modern containers cache singleton-scoped beans and resolve transient ones efficiently, but reflection-based DI is slower than compile-time DI (e.g., Dagger).

**Factories and elasticity** — In cloud-native systems, factories often produce clients to remote services. Choosing the right factory at runtime supports multi-region failover and elastic scaling.

**Horizontal scaling** — When scaling horizontally, ensure singleton-scoped services do not hold per-node state that should be shared. Otherwise, move shared state to distributed caches or databases.

**Fault tolerance** — Factories make it easy to swap a failing implementation for a fallback (e.g., a circuit-breaker-wrapped client).



## Security Considerations

Object creation has direct security implications.

**Singletons holding secrets** — Configuration singletons often hold API keys, database credentials, and tokens. They must be initialized from secure sources (environment variables, secret managers like AWS Secrets Manager or HashiCorp Vault), never hard-coded.

**DI configuration as attack surface** — In some frameworks, DI configuration is driven by classpath scanning or runtime reflection. Malicious classes on the classpath could be injected. Use explicit configuration in security-sensitive applications.

**Deserialization vulnerabilities** — Prototype cloning that uses Java serialization can be exploited to instantiate arbitrary classes. Prefer copy constructors or explicit clone methods.

**Factory poisoning** — A factory whose selection logic relies on user input must validate that input rigorously. Otherwise, an attacker may force creation of a privileged implementation.

**Least privilege in constructed objects** — Objects should be constructed with the minimum permissions they need. DI containers should not inject privileged objects into low-trust components.



## Database Design Considerations

Creational patterns interact heavily with data layers.

**Connection management** — Database connections are typically managed as singletons (or singleton-scoped beans) wrapping connection pools (HikariCP, PgBouncer). Constructing connections per request is catastrophic for performance.

**ORM entity factories** — Object-relational mappers like Hibernate and Entity Framework use factory patterns internally to materialize entities from result sets. Lazy loading uses proxy objects produced by these factories.

**Repository pattern with DI** — Repositories are typically singleton-scoped services injected into other services. They abstract data access behind interfaces.

**Polyglot persistence** — When a system uses multiple databases (SQL for transactions, NoSQL for analytics), factories or abstract factories help select the right client.

**ACID vs BASE choices** — Factories can dynamically pick a strongly consistent (ACID) or eventually consistent (BASE) data store depending on the operation, enabling fine-grained CAP trade-offs.



## Cloud and DevOps Perspective

Modern cloud-native systems use creational patterns at every layer.

**Infrastructure SDKs** — AWS SDK v2 uses Builder patterns extensively (`S3Client.builder().region(...).build()`). Azure and Google Cloud SDKs follow similar conventions.

**Kubernetes client factories** — The Kubernetes Java/Go clients use factories to produce typed API clients (`CoreV1Api`, `AppsV1Api`).

**Dependency injection in Kubernetes operators** — Frameworks like Quarkus and Spring Boot ship DI-aware native images, optimized for serverless and Kubernetes deployments.

**Twelve-Factor App** — The "config" factor (factor III) recommends loading configuration from environment variables, typically via a singleton configuration manager.

**Service mesh integration** — In meshes like Istio, sidecar-injected proxies are created by Kubernetes admission controllers — themselves a form of factory.

**CI/CD pipelines** — Build pipelines often produce different artifacts (Docker images, native binaries) using builder-like declarative steps in YAML.



## Real-World Examples

**Spring Framework (Java)** — Possibly the largest DI container in production worldwide. Every `@Component`, `@Service`, `@Repository` is constructed and wired by Spring's `ApplicationContext`.

**Netflix** — Uses Guice for dependency injection across its microservices. Factories are used to construct Hystrix circuit-breaker-wrapped clients.

**Android (Google)** — Hilt (built on Dagger) provides compile-time DI for mobile applications, avoiding reflection costs.

**Unreal Engine and Unity** — Heavy use of Prototype (cloning prefabs) for spawning game entities.

**Amazon Web Services** — All AWS SDK clients are created via Builder patterns. AWS service clients are typically used as application-scoped singletons.

**Kubernetes** — The control plane uses factory patterns extensively for constructing resource clients and admission webhooks.

**Logging libraries (Log4j, SLF4J, Logback)** — Logger acquisition uses Factory Methods, often returning singletons keyed by logger name.

**OkHttp (Square)** — A canonical example of the Builder pattern for HTTP requests and clients.



## Advantages and Disadvantages

**Advantages**:

- Decouples client code from concrete classes
- Improves testability through substitutable dependencies
- Encapsulates complex construction logic
- Enables runtime polymorphism in object creation
- Supports the Open/Closed Principle for adding new object types
- Centralizes lifecycle management

**Disadvantages**:

- Adds indirection that can obscure simple flows
- Increases the number of classes and configuration files
- DI containers can be hard to debug when misconfigured
- Overuse leads to "pattern soup" — code where patterns hide rather than reveal intent
- Singletons can introduce hidden global state
- Reflection-based DI has runtime cost

**When to use**:

- Object construction is complex, conditional, or has many variations
- Multiple implementations of an abstraction must be selected at runtime
- The system needs to be testable through mocking
- Object lifetimes must be managed centrally

**When not to use**:

- Simple value objects or DTOs that need no abstraction
- Throwaway scripts and one-off tools
- Performance-critical hot paths where indirection costs matter (with measurement)



## Common Interview Questions

**Conceptual**:

- Explain the difference between Factory Method and Abstract Factory.
- Why is Singleton considered an anti-pattern by some engineers?
- What is the difference between constructor injection and setter injection? Which is preferred and why?
- How does the Builder pattern enforce immutability?
- When would you use Prototype over Factory Method?

**Scenario-based**:

- Design a configuration manager that supports loading from files, environment variables, and remote services. Which patterns apply?
- You need to support payment processing across Stripe, PayPal, and Razorpay. Design the architecture.
- How would you implement a thread-safe Singleton in Java without using `synchronized` on every access?
- A legacy class uses `new` everywhere and cannot be unit tested. How do you refactor it?
- Your DI container has a circular dependency error. What strategies can resolve it?

**Architecture**:

- How does Spring's ApplicationContext implement DI internally?
- Compare Dagger (compile-time DI) with Spring (runtime DI). Trade-offs?
- Design an extensible plugin system using creational patterns.
- How do creational patterns interact with the Strategy and Observer patterns?



## Common Mistakes and Misconceptions

**Singleton is a global variable in disguise** — Many engineers reach for Singleton when they should use a DI-managed singleton-scoped bean. The former pollutes global state; the latter is testable and configurable.

**Confusing Factory Method with Abstract Factory** — Factory Method produces a single product type via inheritance. Abstract Factory produces a family of related products, typically via composition.

**Using Builder for trivial objects** — A Builder for a class with two fields adds noise without value. Use Builders when complexity justifies them.

**Deep vs shallow cloning errors in Prototype** — Cloning often produces objects that share mutable internal state with the prototype, leading to subtle bugs.

**Service locator masquerading as DI** — Pulling dependencies from a static context inside a method is service location, not dependency injection. It hides dependencies and harms testability.

**Over-engineering** — Applying patterns reflexively rather than in response to real complexity is one of the most common mistakes among intermediate engineers.

**Treating DI as magic** — DI containers are concrete, debuggable systems. Engineers should understand how their container resolves graphs, not treat it as a black box.



## Diagram Representation

**Overall creational pattern landscape**:

```mermaid
flowchart TD
    Client -->|requests object| CreationMechanism
    CreationMechanism --> Singleton
    CreationMechanism --> FactoryMethod
    CreationMechanism --> AbstractFactory
    CreationMechanism --> Builder
    CreationMechanism --> Prototype
    CreationMechanism --> DIContainer

    Singleton --> Product
    FactoryMethod --> Product
    AbstractFactory --> ProductFamily
    Builder --> ComplexProduct
    Prototype --> ClonedProduct
    DIContainer --> Product
```

**DI request lifecycle in a backend service**:

```mermaid
sequenceDiagram
    participant Client
    participant Controller
    participant Container as DI Container
    participant Service as UserService
    participant Repo as UserRepository
    participant DB as Database

    Client->>Controller: HTTP GET /users/42
    Controller->>Container: resolve(UserService)
    Container->>Container: lookup UserService bean
    Container->>Container: resolve(UserRepository)
    Container-->>Controller: UserService instance
    Controller->>Service: getUser(42)
    Service->>Repo: findById(42)
    Repo->>DB: SELECT * FROM users WHERE id=42
    DB-->>Repo: row
    Repo-->>Service: User entity
    Service-->>Controller: User DTO
    Controller-->>Client: 200 OK
```

**Abstract Factory family selection**:

```mermaid
flowchart LR
    Config[Runtime Config] --> Selector{Platform?}
    Selector -->|Windows| WF[WindowsFactory]
    Selector -->|macOS| MF[MacFactory]
    Selector -->|Linux| LF[LinuxFactory]
    WF --> WUI[Windows UI Family]
    MF --> MUI[Mac UI Family]
    LF --> LUI[Linux UI Family]
```

**Builder construction flow**:

```mermaid
sequenceDiagram
    participant Client
    participant Builder
    participant Product

    Client->>Builder: newBuilder()
    Client->>Builder: url("...")
    Client->>Builder: method("POST")
    Client->>Builder: header("k","v")
    Client->>Builder: body("...")
    Client->>Builder: build()
    Builder->>Product: new(this)
    Product-->>Client: immutable HttpRequest
```



## Admonitions and Notes

!!! warning
    Avoid using Singleton as a substitute for proper dependency management. Hidden global state is one of the leading causes of untestable code in legacy systems.

!!! tip
    When a constructor exceeds four parameters or has many optional fields, reach for the Builder pattern. Modern languages and libraries (Kotlin data classes, Java records, Lombok `@Builder`) make this near-free.

!!! note
    Dependency Injection containers are not "free" — they trade construction simplicity for configuration complexity. Always understand how your container resolves the object graph.

!!! danger
    Never expose factory selection logic directly to untrusted user input without validation. This can lead to instantiation-based security vulnerabilities.

!!! info
    In high-performance systems, compile-time DI (Dagger, Micronaut) often outperforms runtime DI (Spring) because it avoids reflection.



## Code and Configuration Examples

**Spring Boot configuration (YAML)**:

```yaml
spring:
  datasource:
    url: jdbc:postgresql://db:5432/app
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
  jpa:
    hibernate:
      ddl-auto: validate

app:
  notification:
    provider: email
```

**Docker Compose with DI-driven service**:

```yaml
version: "3.9"
services:
  api:
    build: .
    environment:
      - DB_USER=appuser
      - DB_PASSWORD=secret
      - NOTIFICATION_PROVIDER=sms
    depends_on:
      - db
  db:
    image: postgres:16
    environment:
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=secret
```

**Kubernetes deployment using config-driven factory selection**:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 3
  selector:
    matchLabels: { app: user-service }
  template:
    metadata:
      labels: { app: user-service }
    spec:
      containers:
        - name: api
          image: registry/user-service:1.4.0
          env:
            - name: NOTIFICATION_PROVIDER
              value: "email"
            - name: PAYMENT_GATEWAY
              value: "stripe"
```

**Nginx as a reverse proxy in front of a DI-driven app**:

```nginx
upstream user_service {
    server user-service-1:8080;
    server user-service-2:8080;
    server user-service-3:8080;
}

server {
    listen 80;
    location /api/ {
        proxy_pass http://user_service/;
        proxy_set_header Host $host;
    }
}
```



## Performance and Trade-Off Analysis

| Concern | Direct `new` | Factory | Builder | DI Container |
||||||
| Construction speed | Fastest | Fast | Fast | Slower (reflection) |
| Flexibility | Lowest | High | Medium | Highest |
| Testability | Lowest | High | High | Highest |
| Code complexity | Lowest | Medium | Medium | High initially |
| Runtime configurability | None | Medium | Medium | High |

**Key trade-offs**:

- **Simplicity vs flexibility** — Direct instantiation is simple but rigid; DI is flexible but adds infrastructure.
- **Compile-time vs runtime** — Compile-time DI catches errors early but limits dynamism. Runtime DI is more flexible but defers errors.
- **Eager vs lazy creation** — Singletons can be created eagerly (predictable startup) or lazily (faster startup, possible first-request latency).
- **Static vs dynamic factories** — Static factory methods are simple but harder to mock. Instance-based factories are testable but require injection.
- **Cloning vs construction** — Prototype is fast for heavy objects but error-prone with deep references.

Architects choose based on system requirements: a microservice optimized for cold-start latency may avoid heavy DI containers; an enterprise monolith may embrace them.



## Example System Design Walkthrough

**System**: A multi-channel Notification Service.

**Requirements**:

- Support email, SMS, push notifications, and Slack messages.
- New channels must be addable without modifying existing code.
- Each channel has its own configuration (SMTP, Twilio, FCM, webhook URLs).
- The system must be testable in isolation.
- Configuration is environment-driven.

**High-Level Design**:

```mermaid
flowchart LR
    Client[Client Service] --> API[Notification API]
    API --> Dispatcher[NotificationDispatcher]
    Dispatcher --> Factory[NotificationFactory]
    Factory --> Email[EmailNotifier]
    Factory --> SMS[SMSNotifier]
    Factory --> Push[PushNotifier]
    Factory --> Slack[SlackNotifier]
    Container[DI Container] -.->|injects| Dispatcher
    Container -.->|injects| Factory
    Config[(Config Source)] --> Container
```

**Pattern application**:

- **Abstract Factory**: `NotificationFactory` produces a family of `Notifier` implementations.
- **Factory Method**: Each notifier subtype is constructed via a method based on a `ChannelType` enum.
- **Builder**: Each notification message (subject, body, recipients, priority, attachments) is constructed using a `NotificationMessage.Builder`.
- **Singleton (DI-scoped)**: The dispatcher and factory are singleton-scoped beans managed by the DI container.
- **Dependency Injection**: All notifiers are injected into the dispatcher; no hard-coded dependencies.
- **Prototype**: Common message templates (e.g., welcome email) are stored as prototypes and cloned for each user.

**Database Design**:

- `notifications` table (id, user_id, channel, status, payload, sent_at) — relational, ACID.
- `templates` table (id, channel, subject, body_template) — relational.
- A Redis cache stores rate-limit counters per user per channel.

**Scaling Strategy**:

- Horizontal scaling of the API and dispatcher behind a load balancer.
- Queue-based asynchronous dispatch (RabbitMQ or Kafka) so notification spikes do not overload providers.
- Channel-specific worker pools, with retry/backoff per provider.

**Bottlenecks and Optimizations**:

- External provider latency → use circuit breakers and bulkheads.
- Template rendering cost → cache pre-rendered templates as prototypes.
- DI container startup time → use compile-time DI (Micronaut/Quarkus) for cold-start-sensitive deployments.

**Pattern justification**:

- New channels (e.g., WhatsApp) require only a new `Notifier` implementation and a factory entry — OCP is preserved.
- Each notifier can be unit-tested with mocked external clients.
- Configuration changes require no code modifications.



## Practice Questions

**Beginner**:

1. Explain why hard-coding `new SomeClass()` inside business logic makes the code harder to test.
2. Implement a thread-safe Singleton in your preferred language without using double-checked locking.
3. List three real-world scenarios where the Builder pattern is more appropriate than a constructor.

**Intermediate**:

4. Refactor a class with a 10-parameter constructor into a Builder-based design. Identify which fields should be mandatory and which optional.
5. Design a small plugin system where new file format parsers (JSON, XML, YAML) can be added without modifying the core parser interface. Specify which creational patterns you used and why.
6. Compare Factory Method and Abstract Factory using a UI toolkit example. Show class diagrams.
7. Demonstrate how Spring (or any DI container) resolves a transitive dependency graph. Describe what happens when a circular dependency is detected.

**Advanced**:

8. You are designing a multi-tenant SaaS where each tenant requires a different storage backend (Postgres, MongoDB, DynamoDB). Design a creational architecture that selects the correct backend per request without leaking configuration into business code.
9. Discuss the trade-offs of compile-time DI (Dagger, Micronaut) versus runtime DI (Spring). Under what conditions would you choose each?
10. A high-frequency trading system rejects all forms of dynamic dispatch and reflection for latency reasons. How would you achieve the benefits of DI without using a runtime container?



## Real-World Applications and Use Cases

- **Enterprise web services** — DI-driven Spring Boot and ASP.NET Core applications form the backbone of corporate IT.
- **Cloud SDKs** — AWS, Azure, and GCP client libraries are textbook examples of Builder and Factory usage.
- **Mobile applications** — Hilt/Dagger on Android, and Swift's property wrappers, structure dependency graphs at compile time.
- **Game engines** — Unity prefabs and Unreal Engine blueprint instances use Prototype for entity spawning.
- **CI/CD systems** — Jenkins, GitHub Actions, and GitLab CI use builder-style declarative configuration.
- **API frameworks** — FastAPI's `Depends`, NestJS's modules, and Angular's injectors implement DI for HTTP applications.
- **Microservice meshes** — Service discovery and client construction in Istio, Linkerd, and Consul rely on factory abstractions.
- **Testing frameworks** — Mockito, Moq, and unittest.mock leverage DI to substitute test doubles for real dependencies.



## Summary

Creational design patterns control *how* objects come into existence, separating construction from use. They are foundational to building large-scale systems that remain flexible, testable, and maintainable over time.

The six patterns covered each address a distinct construction concern:

- **Singleton** ensures a single, shared instance.
- **Factory Method** delegates instantiation choice to subclasses.
- **Abstract Factory** produces families of related objects.
- **Builder** constructs complex objects step-by-step with readable, immutable APIs.
- **Prototype** clones pre-configured objects instead of building from scratch.
- **Dependency Injection** inverts control, letting external infrastructure supply dependencies.

The most important architectural lessons:

- Direct instantiation tightly couples components and limits flexibility.
- Patterns are tools, not goals — use them when complexity justifies them.
- Dependency Injection underpins almost every modern enterprise framework.
- Singleton is the most overused and most misused pattern; prefer DI-managed singletons.
- Creational decisions ripple outward into scalability, testability, and security.

A skilled architect uses creational patterns deliberately: enough to manage real complexity, but no more. The goal is not to apply patterns for their own sake, but to produce systems that survive years of evolving requirements.


