| name | creational |
| description | Creational design patterns from the Gang of Four — Factory Method, Abstract Factory, Builder, Prototype, and Singleton. Patterns that abstract the instantiation process to make systems independent of how objects are created, composed, and represented.
USE FOR: object creation, factory patterns, builder pattern, prototype cloning, singleton instances, decoupling instantiation from usage
DO NOT USE FOR: composing/adapting objects (use structural), inter-object communication (use behavioral)
|
| license | MIT |
| metadata | {"displayName":"Creational Patterns","author":"Tyler-R-Kendrick"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"Refactoring.Guru — Creational Design Patterns","url":"https://refactoring.guru/design-patterns/creational-patterns"},{"title":"Creational Pattern — Wikipedia","url":"https://en.wikipedia.org/wiki/Creational_pattern"}] |
Creational Design Patterns
Overview
Creational patterns abstract the instantiation process. They help make a system independent of how its objects are created, composed, and represented. As systems evolve to depend more on object composition than class inheritance, the emphasis shifts from hard-coding fixed sets of behaviors toward defining a smaller set of fundamental behaviors that can be composed into more complex ones — and that requires flexible object creation.
1. Factory Method
Intent
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Creator │ │ Product │
│ (abstract) │ │ (interface) │
├──────────────────┤ └──────┬───────────┘
│ + factoryMethod() │ │
│ + someOperation() │ │
└──────┬───────────┘ │
│ │
│ extends │ implements
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ ConcreteCreator │────────▶│ ConcreteProduct │
├──────────────────┤ creates └──────────────────┘
│ + factoryMethod() │
└──────────────────┘
Participants
- Product — declares the interface for created objects
- ConcreteProduct — implements the Product interface
- Creator — declares the factory method returning a Product; may define a default implementation
- ConcreteCreator — overrides the factory method to return a ConcreteProduct
When to Use
- A class cannot anticipate the class of objects it must create
- A class wants its subclasses to specify the objects it creates
- You want to localize the knowledge of which helper class is the delegate
TypeScript Example
interface Transport {
deliver(cargo: string): string;
}
class Truck implements Transport {
deliver(cargo: string): string {
return `Delivering "${cargo}" by road in a truck`;
}
}
class Ship implements Transport {
deliver(cargo: string): string {
return `Delivering "${cargo}" by sea in a ship`;
}
}
abstract class Logistics {
abstract createTransport(): Transport;
planDelivery(cargo: string): string {
const transport = this.createTransport();
return transport.deliver(cargo);
}
}
class {
(): {
();
}
}
{
(): {
();
}
}
: = ();
.(logistics.());
2. Abstract Factory
Intent
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Structure
┌─────────────────────┐
│ AbstractFactory │
│ (interface) │
├─────────────────────┤
│ + createProductA() │
│ + createProductB() │
└──────┬──────────────┘
│ implements
▼
┌─────────────────────┐ ┌──────────────┐ ┌──────────────┐
│ ConcreteFactory1 │────▶│ ProductA1 │ │ ProductB1 │
├─────────────────────┤ └──────────────┘ └──────────────┘
│ + createProductA() │
│ + createProductB() │ ┌──────────────┐ ┌──────────────┐
├─────────────────────┤ │ ProductA2 │ │ ProductB2 │
│ ConcreteFactory2 │────▶└──────────────┘ └──────────────┘
├─────────────────────┤
│ + createProductA() │
│ + createProductB() │
└─────────────────────┘
Participants
- AbstractFactory — declares creation methods for each abstract product
- ConcreteFactory — implements creation methods for a specific product family
- AbstractProduct — declares an interface for a type of product
- ConcreteProduct — implements the abstract product for a specific family
- Client — uses only the AbstractFactory and AbstractProduct interfaces
When to Use
- A system should be independent of how its products are created
- A system should be configured with one of multiple families of products
- A family of related objects is designed to be used together and you need to enforce that constraint
TypeScript Example
interface Button {
render(): string;
}
interface Checkbox {
toggle(): string;
}
interface UIFactory {
createButton(): Button;
createCheckbox(): Checkbox;
}
class MaterialButton implements Button {
render(): string { return "<MaterialButton />"; }
}
class MaterialCheckbox implements Checkbox {
toggle(): string { return "Material checkbox toggled"; }
}
class FluentButton implements Button {
render(): string { return "<FluentButton />"; }
}
class FluentCheckbox implements Checkbox {
toggle(): string { return "Fluent checkbox toggled"; }
}
{
(): { (); }
(): { (); }
}
{
(): { (); }
(): { (); }
}
() {
button = factory.();
checkbox = factory.();
{ : button.(), : checkbox.() };
}
ui = ( ());
.(ui.);
3. Builder
Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
Structure
┌──────────────┐ ┌───────────────────┐
│ Director │──────▶│ Builder │
├──────────────┤ │ (interface) │
│ + construct() │ ├───────────────────┤
└──────────────┘ │ + buildPartA() │
│ + buildPartB() │
│ + getResult() │
└──────┬────────────┘
│ implements
▼
┌───────────────────┐ ┌─────────┐
│ ConcreteBuilder │──────▶│ Product │
├───────────────────┤builds └─────────┘
│ + buildPartA() │
│ + buildPartB() │
│ + getResult() │
└───────────────────┘
Participants
- Builder — specifies an abstract interface for creating parts of a Product
- ConcreteBuilder — constructs and assembles parts; provides a method to retrieve the result
- Director — constructs an object using the Builder interface
- Product — the complex object under construction
When to Use
- The algorithm for creating a complex object should be independent of the parts and their assembly
- The construction process must allow different representations of the constructed object
- You want to avoid "telescoping constructor" anti-pattern (constructors with many parameters)
TypeScript Example
class HttpRequest {
method: string = "GET";
url: string = "";
headers: Record<string, string> = {};
body?: string;
timeout: number = 30000;
toString(): string {
return `${this.method} ${this.url} | headers=${JSON.stringify(this.headers)} | body=${this.body ?? "none"}`;
}
}
class HttpRequestBuilder {
private request = new HttpRequest();
setMethod(method: string): this {
this.request.method = method;
return this;
}
setUrl(url: string): this {
this.request. = url;
;
}
(: , : ): {
..[key] = value;
;
}
(: ): {
.. = body;
;
}
(: ): {
.. = ms;
;
}
(): {
result = .;
. = ();
result;
}
}
{
() {}
(: , : ): {
.
.()
.(url)
.(, )
.(, )
.(.(body))
.();
}
}
builder = ();
director = (builder);
request = director.(, { : });
.(request.());
4. Prototype
Intent
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Structure
┌───────────────────┐
│ Prototype │
│ (interface) │
├───────────────────┤
│ + clone(): Proto │
└──────┬────────────┘
│ implements
▼
┌───────────────────┐ ┌───────────────────┐
│ ConcretePrototype1 │ │ ConcretePrototype2 │
├───────────────────┤ ├───────────────────┤
│ + clone(): Proto │ │ + clone(): Proto │
└───────────────────┘ └───────────────────┘
│ │
│ clone() │ clone()
▼ ▼
[Copy of 1] [Copy of 2]
Participants
- Prototype — declares an interface for cloning itself
- ConcretePrototype — implements the cloning operation
- Client — creates new objects by asking a prototype to clone itself
When to Use
- Classes to instantiate are specified at runtime (e.g., dynamic loading)
- You want to avoid building a hierarchy of factory classes that parallels the product hierarchy
- Instances of a class can have one of only a few different combinations of state — cloning preset prototypes is more convenient than instantiating manually
TypeScript Example
interface Cloneable<T> {
clone(): T;
}
class GameUnit implements Cloneable<GameUnit> {
constructor(
public name: string,
public health: number,
public attack: number,
public position: { x: number; y: number }
) {}
clone(): GameUnit {
return new GameUnit(
this.name,
this.health,
this.attack,
{ ...this.position }
);
}
toString(): string {
return `${this.name}(hp=${this.health}, atk=${this.attack}) @ (${this.position.x},)`;
}
}
{
prototypes = <, >();
(: , : ): {
..(key, prototype);
}
(: ): {
proto = ..(key);
(!proto) ();
proto.();
}
}
registry = ();
registry.(, (, , , { : , : }));
registry.(, (, , , { : , : }));
unit1 = registry.();
unit1. = { : , : };
unit2 = registry.();
unit2. = { : , : };
.(unit1.());
.(unit2.());
5. Singleton
Intent
Ensure a class has only one instance and provide a global point of access to it.
Structure
┌─────────────────────────────┐
│ Singleton │
├─────────────────────────────┤
│ - static instance: Singleton │
│ - data: ... │
├─────────────────────────────┤
│ - constructor() │
│ + static getInstance() │
│ + businessMethod() │
└─────────────────────────────┘
Participants
- Singleton — defines a static method that returns its sole instance; the constructor is private to prevent external instantiation
When to Use
- There must be exactly one instance of a class, and it must be accessible from a well-known access point
- The sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code
Why Singleton Is Often an Anti-Pattern
- Global mutable state: Singletons are effectively global variables, making behavior unpredictable and hard to trace
- Hidden dependencies: Classes that use a Singleton do not declare that dependency in their constructor
- Testing difficulty: Singletons carry state between tests, causing flaky test suites and order-dependent failures
- Concurrency hazards: Lazy initialization of singletons is a classic source of race conditions
- Prefer dependency injection: In modern systems, use a DI container to register a service with singleton lifetime. This gives you single-instance behavior without the drawbacks
TypeScript Example
class AppConfig {
private static instance: AppConfig | null = null;
private constructor(
public readonly dbHost: string,
public readonly dbPort: number,
public readonly logLevel: string
) {}
static getInstance(): AppConfig {
if (!AppConfig.instance) {
AppConfig.instance = new AppConfig(
process.env.DB_HOST ?? "localhost",
Number(process.env.DB_PORT ?? 5432),
process.env.LOG_LEVEL ?? "info"
);
}
return AppConfig.instance;
}
static resetForTesting(): {
. = ;
}
}
config = .();
.(config.);
Comparison Table
| Pattern | Scope | Key Benefit | Typical Use Case |
|---|
| Factory Method | Class | Defers creation to subclasses | Frameworks that define interfaces but let apps decide implementations |
| Abstract Factory | Object | Creates families of related objects | Cross-platform UI toolkits, themed component libraries |
| Builder | Object | Step-by-step construction with fluent API | Complex objects with many optional parameters (HTTP requests, queries) |
| Prototype | Object | Cloning pre-configured instances | Game units, document templates, expensive-to-create objects |
| Singleton | Object | Guarantees single instance | Configuration, connection pools (prefer DI singleton lifetime) |
Decision Guide
Do you need to create objects?
│
├─ One product, varies by subclass?
│ └─▶ Factory Method
│
├─ Families of related products?
│ └─▶ Abstract Factory
│
├─ Complex object with many parts/options?
│ └─▶ Builder
│
├─ Copy an existing configured object?
│ └─▶ Prototype
│
└─ Exactly one instance, globally accessible?
└─▶ Singleton (but strongly consider DI singleton lifetime instead)