TheHowPage
Interactive Explainer

Creational Design Patterns Factory, Builder, Singleton & Prototype

Every time you write new, you're making a design decision. Most of them will haunt you later.

Factory.create()newnewnewnewnewnewPaymentProcessorEmailSenderLoggerConfig

Scattered new — tightly coupledFactory pattern — decoupled creation

Scroll to explore ↓

What Is a Design Pattern?

A design pattern is a named solution to a problem that keeps showing up in software. It's not a library you install or a framework you configure — it's an approach, a template that experienced developers recognized and gave a name to.

Think of it like cooking techniques. “Sautéing” isn't a recipe — it's a technique you apply to hundreds of recipes. Similarly, the “Factory pattern” isn't specific code — it's a technique you apply to hundreds of situations.

In 1994, four computer scientists (nicknamed the Gang of Four, or GoF) cataloged 23 of these patterns in a book called Design Patterns. Thirty years later, the patterns are still used daily in every modern language. They fall into three categories:

Creational

How objects get made

← this chapter

Structural

How objects connect

Behavioral

How objects communicate

New terms in this chapter

Concrete class —
A specific, actual class like EmailSender or StripeProcessor. You can create objects from it directly.
Client code —
The part of your code that uses objects, not creates them. The checkout page is “client code” — it doesn't care how the payment processor was created, it just uses it.
Coupling —
How tightly one piece of code depends on another. new StripeProcessor() in 15 files = tight coupling. Change Stripe, change 15 files.
Fluent API —
A style where each method returns the object itself, so you can chain calls: .addCheese().addSauce().build()

The Problem with new

Every time you write new EmailSender(), you're making a decision: “I need this exact class, right here, right now.” That works when your app is small. But watch what happens when it grows:

📅

Month 1

Your app sends email notifications. You write new EmailSender() in 15 places — checkout, signup, password reset, order confirmation, etc.

📅

Month 3

The boss says “add SMS notifications.” Now you're editing 15 files, adding if-else checks everywhere: if (type === "email") new EmailSender() else new SMSSender()

📅

Month 6

Add push notifications. Then Slack. Then WhatsApp. Each new channel means editing those same 15 files. Each edit risks breaking something that was already working.

Creational patterns solve this. Instead of scattering new everywhere, you centralize object creation in one place. Add a new notification channel? Change one file. The rest of your app doesn't even know it happened.

The Factory — Stop Writing new Everywhere

A Factory is a function or class whose only job is to create objects. Instead of your code saying new StripeProcessor(), it asks the factory: “Give me a payment processor.” The factory decides which one to create.

Created: CreditCardProcessor
payment-service.tsFRAGILE
TypeScript
1function createProcessor(type) {
2 if (type === "creditCard")
3 return new CreditCardProcessor()
4 else if (type === "paypal")
5 return new PayPalProcessor()
6 else if (type === "crypto")
7 return new CryptoProcessor()
8 // Adding Apple Pay? Edit THIS function.
9}

Cost of Adding a New Payment Type

Files to edit

Without
1+ (but risky)
With
0 existing

Lines changed

Without
15 lines
With
1 line (registration only)

You've Already Used Factories

The Factory pattern is so common that most languages and frameworks have it built in — you just never noticed:

document.createElement("div")

The browser's built-in factory. You don't call new HTMLDivElement() — the factory decides the right class based on the tag name.

React.createElement("button", props)

React's factory. It creates the right internal element — could be a DOM node, a custom component, or a fragment. You don't choose.

Calendar.getInstance()

Java's factory. Returns GregorianCalendar in the US, JapaneseCalendar in Japan, BuddhistCalendar in Thailand. Same call, different object based on locale.

Angular's HttpClient

Angular's DI container is a giant factory. You ask for HttpClient, it creates one with the right interceptors, base URL, and retry logic already configured.

The Abstract Factory takes this further — instead of creating one object, it creates a family of objects that belong together. Think of a design system: if you switch from a “Material” theme to an “iOS” theme, every component — buttons, inputs, modals — must change together. An Abstract Factory ensures they all match. This is how Tailwind themes, Material UI, and Ant Design work under the hood.

The Builder — Step by Step

Sometimes creating an object requires many steps. A Builder lets you construct an object piece by piece, instead of passing 12 confusing arguments to a constructor.

Think of ordering a custom sandwich. You don't shout all your choices at once — you build it step by step.

Build Your Sandwich
Bread
Protein
Cheese
Extras
Toasted?
Preview
Pick some ingredients...
sandwich-builder.tsReadable
TypeScript
1new SandwichBuilder()
2 .build()

Builder Is Everywhere You Look

Any time you see a chain of method calls ending in .build(), that's the Builder pattern. Some famous examples:

Rust

Command::new("ls").arg("-la").current_dir("/tmp").spawn()

Rust uses builders everywhere because it doesn't have default parameters. The standard library's Command, Thread::Builder, and many more follow this pattern.

Java

new Request.Builder().url(url).header("Auth", token).build()

OkHttp (used by millions of Android apps) builds HTTP requests step by step. Each step is optional and named.

SQL

db.select("*").from("users").where("age > 21").limit(10)

Query builders in every ORM (Prisma, Sequelize, TypeORM, Django) use the Builder pattern to construct SQL dynamically.

Joshua Bloch, author of Effective Java (one of the most influential programming books ever written), put it simply: “Consider a builder when faced with many constructor parameters.” If your constructor has more than 3-4 parameters, a builder will make your code more readable and less error-prone.

When NOT to use it: if your object has 2-3 fields, just use a constructor. A builder for a Point(x, y) is over-engineering.

The Singleton — There Can Be Only One

A Singleton ensures a class has exactly one instance — and gives everyone a way to access it.

Think of a country's president. There's only one at a time. Everyone refers to "the president" — they don't each elect their own.

Common uses: database connection pools, loggers, app configuration.

Thread A
Idle
Database Singleton
No instance
Thread B
Idle
singleton-broken.tsBroken
TypeScript
1class Database {
2 static instance = null
3
4 static getInstance() {
5 if (!this.instance)
6 this.instance = new Database()
7 return this.instance
8 }
9}

Why Experienced Developers Argue About Singleton

Singleton is the only design pattern that experienced developers actively argue against. The pattern itself is simple — ensure one instance, provide global access. The problems are subtle:

  • 1.Hidden dependencies — any code anywhere can access the singleton. When something breaks, you have no idea which of the 50 files that touch the singleton caused it. It's like having a shared whiteboard that everyone writes on — good luck figuring out who drew the inappropriate picture.
  • 2.Testing nightmare — you can't replace a singleton with a mock (a fake version for testing). Your unit tests accidentally talk to a real database, send real emails, or modify real data. Tests become slow, fragile, and dangerous.
  • 3.Ordering problems — if Singleton A depends on Singleton B, which gets created first? In complex apps, this leads to “initialization order fiasco” bugs that only appear in production.

The Modern Alternative: Dependency Injection

Modern frameworks like Spring (Java), NestJS (TypeScript), and Angular solve this differently. They register services as singletons through Dependency Injection (DI) — the framework creates one instance and passes it to every class that needs it. You get the “one instance” guarantee without global state, without hidden dependencies, and with easy testing (just inject a mock).

Kotlin went even further: the object keyword creates a language-level singleton — thread-safe, no boilerplate, no pattern needed.

Rule of thumb: use Singleton for logging, app configuration, and connection pools. For everything else, use Dependency Injection.

The Prototype — Copy, Don't Create

Sometimes creating an object from scratch is expensive. The Prototype pattern says: just copy an existing one.

Think of photocopying a filled-out form. Filling out the first one took 30 minutes. Making copies takes seconds.

Original

Warrior

Health:100
Position:(10, 20)
Inventory:
SwordShieldPotion
shallow-clone.tsShallow
TypeScript
1const clone = { ...original };
2
3// or equivalently:
4const clone = Object.assign({}, original);
5
6// clone.inventory === original.inventory
7// They share the SAME array reference!
deep-clone.tsDeep
TypeScript
1const clone = structuredClone(original);
2
3// clone.inventory !== original.inventory
4// Fully independent copy — nested objects too
5
6// Added to JavaScript in 2022 (ES2024 spec)
7// Works in all modern browsers + Node 17+

Cloning Is More Common Than You Think

You've been using the Prototype pattern without knowing it:

  • JavaScript's entire object system is prototype-based. Object.create() literally creates an object using another object as its prototype. When you access a property that doesn't exist on the object, JavaScript walks up the “prototype chain” to find it.
  • React's state immutability is conceptually this pattern. Every setState() creates a new version of your state — the old version is untouched. React compares the old and new to decide what to re-render.
  • Game development uses this constantly. You define one “enemy template” and clone it 1000 times, modifying each clone's health, position, and behavior. Creating each enemy from scratch would be far too slow.
  • Document editors — every “duplicate slide” in Google Slides or “duplicate layer” in Photoshop is a prototype clone.

Which Pattern Do I Need?

Ask yourself one question, and the answer points to a pattern:

I need to create different objects based on input or config

Factory

I need a family of related objects that all match (like a theme)

Abstract Factory

The object has many optional settings and a complex setup

Builder

I need exactly one instance shared across the whole app

Singleton (or better: Dependency Injection)

Creating from scratch is expensive and I have a similar object

Prototype (clone it)

In practice, Factory and Builder are by far the most common — you'll use them weekly. Singleton shows up in infrastructure code. Prototype is niche but powerful when you need it. Now let's see if you can recognize them in real scenarios.

Pattern Recognition — Can You Spot It?

5 real-world scenarios. Which creational pattern fits each one?

Question 1 of 50 correct so far

Your e-commerce app needs to support Stripe, PayPal, and Razorpay. Each has a different API, but your checkout code should work the same regardless of which one is used. How do you create the right processor?

What's Next

You've learned how objects get created — with Factories, Builders, Singletons, and Prototypes. The next chapter covers how objects get connected: Structural patterns. You'll learn the Adapter (plug incompatible things together), Decorator (add features without changing the original), Facade (simplify a messy system), and more.

What is a design pattern in simple terms?

A design pattern is a named, reusable solution to a problem that keeps showing up in software. It's not a library or framework you install — it's an approach, a template for solving a specific type of problem. The Gang of Four cataloged 23 of them in 1994, and they're still used in every modern language. Think of them like cooking techniques: 'sautéing' isn't a recipe, it's a technique you apply to many recipes.

When should I use the Factory pattern vs just calling new?

Use Factory when: (1) the exact class might change based on input or configuration — like creating a payment processor based on user's choice, (2) you're creating the same type of object in many places — centralizing creation means one change, not fifteen, or (3) the creation logic is complex with setup steps. If you're creating a simple object in one place and the class will never change, just use new. Don't over-engineer.

What's the difference between Factory Method and Abstract Factory?

Factory Method creates one type of object — you call PaymentFactory.create('stripe') and get a StripeProcessor. Abstract Factory creates a family of related objects that belong together — you call UIFactory.create() and get a matching Button + Input + Modal that all share the same theme. Use Factory Method when you need one product. Use Abstract Factory when you need a coordinated set of products (like a design system with light/dark themes).

Why is the Builder pattern so common in Rust?

Rust doesn't have function overloading or default parameter values — you can only have one function with a given name, and every parameter must be explicitly provided. This makes constructors with many optional fields painful. Builders solve this perfectly: Command::new('ls').arg('-la').current_dir('/tmp').spawn(). Each method is optional and named, so you only specify what you need. Rust's standard library uses builders extensively — std::process::Command, std::thread::Builder, and many more.

Is Singleton really an anti-pattern?

It depends who you ask. The problems are real: Singletons create hidden global state, make unit testing difficult (you can't easily swap in a mock), and create ordering dependencies (which singleton initializes first?). Modern frameworks like Spring, NestJS, and Angular solve the 'one instance' need through Dependency Injection instead — you get the same guarantee without the downsides. That said, Singletons are still valid for truly ambient concerns like logging, app configuration, and connection pools. The rule: if you're reaching for Singleton, first ask if DI can do it better.

What's the difference between shallow copy and deep copy?

A shallow copy creates a new object but copies references to nested objects — the original and copy share the same inner objects. Change a nested array in the copy, and the original changes too. A deep copy creates a completely independent clone — nested objects are also copied, not shared. In JavaScript: spread (...) and Object.assign() are shallow. structuredClone() (added in 2022) is deep. JSON.parse(JSON.stringify(obj)) is deep but breaks on Dates, Maps, Sets, and circular references.

How do I choose between Factory, Builder, and Prototype?

Ask three questions: (1) Do I need to pick which class to create at runtime? → Factory. (2) Does the object need many configuration steps? → Builder. (3) Is creating from scratch expensive and I already have a similar object? → Prototype. In practice, Factory is the most common (you'll use it daily), Builder is second (any time parameters explode), and Prototype is niche (game dev, document cloning, caching).

Does JavaScript/TypeScript use these patterns?

Constantly. document.createElement() is a Factory. React.createElement() is a Factory. fetch() API's Request object uses a Builder-like options pattern. JavaScript's entire object system is prototype-based — Object.create() literally creates objects using another object as a prototype. Module-level variables in ES modules are effectively Singletons (the module is evaluated once, the export is shared). You've been using these patterns without knowing their names.

What's Dependency Injection and how does it replace Singleton?

Dependency Injection (DI) means passing dependencies into a class from the outside, instead of the class creating them itself. Instead of OrderService calling new MySQLDatabase() internally (tightly coupled), you pass the database in: new OrderService(database). A DI container (like Spring, NestJS, or Angular's injector) manages this automatically — it creates one instance of Database and injects it everywhere. You get the 'one instance' guarantee of Singleton, but the class doesn't know or care. It just receives what it needs.

Do I need to memorize all design patterns for interviews?

No. Focus on the ones that come up naturally in LLD problems: Factory (creating objects based on type), Builder (complex construction), Observer (event handling), Strategy (swapping algorithms), and State (state machines). Interviewers don't ask 'explain the Prototype pattern.' They give you a problem, and you recognize that the solution IS a pattern. Understanding the principles matters more than memorizing names. If you know SOLID from Chapter 1, you'll often reinvent patterns on your own.

Sources

Every explainer is free. No ads, no paywall, no login.

If this helped you, consider supporting the project.

Buy us a coffee