Skip to content
aipatterns.dev

Category

Creational Patterns

Patterns that control how objects get built, so callers depend on what they need rather than on which concrete class supplies it.

What these patterns solve

Every new ConcreteThing() is a hard dependency. It fixes the class, the constructor arguments and often the lifetime of the object at the call site. Creational patterns move that decision somewhere else: into a method, an object, a step-by-step assembler or a copy of an existing instance. The caller asks for something that satisfies an interface and stops caring how it was made.

Reach for a creational pattern only when construction itself is the thing that varies or hurts. Most code should keep calling constructors directly.

Members

  • Factory Method: a method, overridable in subclasses or passed as a function, decides which concrete product to create.
  • Abstract Factory: one object creates a whole family of related products that must be used together, such as one cloud provider's storage, queue and secrets clients.
  • Builder: assembles a complex object in named steps, so you never call a constructor with twelve positional arguments.
  • Prototype: creates new objects by copying a configured instance instead of rebuilding it from scratch.
  • Singleton: guarantees one instance of a class with a global access point. Usually better replaced by dependency injection.

How to choose within this category

  • One product, and the concrete type depends on context: use Factory Method. In TypeScript, Python, Go or modern Java, a plain function that returns the interface (createLogger(env)) is usually enough. Subclassing a creator class is rarely needed.
  • Several products that must match each other: use Abstract Factory. With only one product, it collapses into Factory Method.
  • Many optional parameters or a multi-step, validated assembly: use Builder. In languages with named or default arguments (Python, Kotlin) or object literals (TypeScript), an options object often removes the need. Keep Builder for cases where order matters or the result must be validated before it exists.
  • Construction is expensive or the configuration is only known at runtime: use Prototype. Check first that your language's copy tools (structuredClone, copy.deepcopy, records with with) don't already do the job.
  • Exactly one shared resource: create it once at the composition root and inject it. Treat Singleton as a last resort, because it hides dependencies and makes tests share state.

See Factory comparison for the differences between the factory variants.

Prefer no pattern when

  • The concrete class never changes. Call the constructor.
  • A module-level function or a dependency injection container already decides what to build.
  • You're adding a factory "in case" a second implementation appears. Wait until it does; that urge is Speculative Generality.

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.