Skip to content

Factory Method

Centralising the decision of which concrete thing to build — and the three different things people mean when they say "factory".

Intermediate17 minUpdated 8 August 2026

Three different structures get called "a factory", and conflating them is why the pattern has a reputation for being pointless ceremony. This topic separates them, then argues that the smallest one is usually the right one.

The problem all three attack is the same: a caller that has to know which concrete class to build is coupled to every class it might build, and construction logic that lives at the call site gets duplicated at every call site.

// Scattered through the codebase, in four different files
const client = config.provider === 'stripe'
  ? new StripeClient(config.key, { timeoutMs: 5000, retries: 3 })
  : new RazorpayClient(config.key, { timeout: 5000 });

Every file that does this knows both vendors exist, knows their constructor shapes, and has its own copy of the retry policy. Adding a third vendor is a search-and-replace.

The three things called "factory"

Conceptual diagramnoyesyesnocaller choosessubclass choosesDoes creation involvea real decision?Just call new.A factory adds nothingDo several objects have tomatch each other?Abstract Factoryone call yields a whole familyShould a subclass choose,or a caller?Simple factoryone function, one switchFactory Methodthe base class calls down
Diagram, as text
  • Does creation involve a real decision? leads to Just call new. A factory adds nothing (no)
  • Does creation involve a real decision? leads to Do several objects have to match each other? (yes)
  • Do several objects have to match each other? leads to Abstract Factory one call yields a whole family (yes)
  • Do several objects have to match each other? leads to Should a subclass choose, or a caller? (no)
  • Should a subclass choose, or a caller? leads to Simple factory one function, one switch (caller chooses)
  • Should a subclass choose, or a caller? leads to Factory Method the base class calls down (subclass chooses)

A simple factory is one function with the decision inside. It is not in the classic catalogue and it is what you want most of the time.

Factory Method is the catalogue entry: a base class defines an operation that creates something, and subclasses decide what. The base class never names a concrete product.

Abstract Factory creates families of related objects that must be consistent with one another. It gets its own topic next.

The simple factory

One place decides, and every caller stops knowing:

export function createPaymentClient(config) {
  switch (config.provider) {
    case 'stripe':
      return new StripeClient(config.key, { timeoutMs: 5000, retries: 3 });
    case 'razorpay':
      return new RazorpayClient(config.key, { timeout: 5000 });
    default:
      throw new Error(`Unknown payment provider: ${config.provider}`);
  }
}

Three things this bought, and it is worth being precise because people claim it buys more:

  • The retry policy exists once. That is the duplication actually removed.
  • Callers name a capability, not a vendor. They hold whatever createPaymentClient returns.
  • Adding a provider edits one file. The default branch means an unknown value fails loudly at startup rather than producing undefined and a null-reference error three layers away.

What it did not buy: the switch statement still exists. A factory does not delete the conditional, it relocates it to one place and runs it once. That is the entire trick, and it is enough.

Factory Method proper

Factory Method appears when a base class owns an algorithm but must not know what it is operating on.

classDiagram
    class ReportJob {
        +run(rows) void
        #createFormatter()* Formatter
    }
    class PdfReportJob {
        #createFormatter() Formatter
    }
    class CsvReportJob {
        #createFormatter() Formatter
    }
    class Formatter {
        <<interface>>
        +format(rows) bytes
    }
    ReportJob <|-- PdfReportJob
    ReportJob <|-- CsvReportJob
    ReportJob ..> Formatter
class ReportJob {
  run(rows) {
    const formatter = this.createFormatter();   // the factory method
    const bytes = formatter.format(rows);
    this.upload(bytes);
    this.recordMetrics(rows.length);
  }
  createFormatter() { throw new Error('subclass must implement'); }
}

class CsvReportJob extends ReportJob {
  createFormatter() { return new CsvFormatter(); }
}

ReportJob owns the sequence — format, upload, record — and never names a formatter. This is the Hollywood principle again: the base class calls down at the one point that varies.

Notice what makes this Factory Method rather than a simple factory: the choice is bound by which subclass you instantiated, not by an argument. That is a real difference in where the decision lives, and it is why Factory Method is often a Template Method with one overridable step that happens to return an object.

When a factory earns nothing

Construction is a plain new. createUser(name, email) returning new User(name, email) is a rename with extra indirection.

There is one implementation. The switch has one arm. You have added a file to express a constant.

The caller already knows which one it wants. If every call site passes 'stripe' explicitly, the decision was never centralised — it was just moved into a string.

The honest test: can you name the decision the factory makes? "It picks the provider from configuration" is a decision. "It calls the constructor" is not.

Where the object graph actually gets built

A factory answers "which class", not "who wires the whole application". Those get confused constantly.

In practice a system has a composition root — one place, usually near start-up, where configuration is read and the object graph is assembled. Factories live there. Everything below receives its collaborators rather than reaching for a factory, because a class calling a factory has simply swapped one hidden dependency for another.

Check your understanding

Common mistakes

Factories that only call new. If construction involves no decision, the factory is a rename.

A factory per class. UserFactory, OrderFactory, InvoiceFactory, each with one method that constructs one thing. This is a naming convention, not a design.

Calling factories deep inside business logic. It hides the dependency and forces tests to fake the factory rather than the collaborator.

No default branch. An unrecognised provider string returning undefined fails far from its cause. Throw at the decision point.

Subclassing purely to change what one method returns. Pass the object in. Inheritance is a heavy way to express a value.

Naming the class after the pattern. PaymentClientFactoryImpl describes the mechanism; PaymentClients or createPaymentClient describes the job.

Practice

Your codebase has 14 classes ending in `Factory`. Twelve of them have a single method that calls one constructor with the same arguments it was given. What do you do, and how do you argue for it?

Write your own answer first, on paper or in a note. Then compare.

Show a hint

Separate the two questions: is the indirection buying anything, and is deleting it safe?

Show a strong answer

First, sort them, because "delete all factories" is the argument that loses. The twelve pass-throughs and the two real ones are different cases and should be handled differently.

For the twelve pass-throughs, name the cost concretely:

  • Every construction site reads through two files instead of one, and the second file contains no information — it repeats the constructor's parameter list and forwards it.
  • They give a false signal. A reader who knows the pattern sees Factory and assumes a decision is being made. Finding there is none costs them the time it took to look, every time.
  • They are load-bearing in tests for the wrong reason. Tests often stub the factory rather than passing a fake object, so deleting one breaks tests that were testing the indirection rather than the behaviour.

The argument that works is not "this is over-engineering" but "name the decision": for each factory, ask what choice it makes. Twelve of them cannot answer. Two can — those stay.

How to do it safely, because a 12-file deletion in one commit will not get merged:

  • Inline them one at a time, each its own commit, each reversible.
  • Start with the one with fewest call sites to establish the pattern and the review precedent.
  • Where a test stubs the factory, change the test first to pass a fake collaborator directly. That change is independently correct and makes the deletion trivial.

What I would keep even without a decision: a factory whose job is to enforce a construction invariant — validating arguments, or guaranteeing the object is registered somewhere before anyone sees it. That is a decision, just not a branching one, and inlining it would scatter the invariant.

A report exporter supports PDF, CSV and HTML. Someone proposes a `ReportJob` base class with a `createFormatter()` factory method and three subclasses. Argue for the alternative, then say what would make the subclass version correct after all.

Write your own answer first, on paper or in a note. Then compare.

Show a hint

Count what actually varies between the three cases.

Show a strong answer

The alternative: one class, formatter injected.

class ReportJob {
  constructor(formatter) { this.formatter = formatter; }
  run(rows) { this.upload(this.formatter.format(rows)); }
}

Why it is better here:

  • Only the formatter varies. Three subclasses exist to express one value each — that is inheritance used to carry a parameter.
  • The choice becomes runtime. A user can pick a format from a dropdown. With subclasses, something must map the string to a class anyway, so you end up with a factory and three subclasses.
  • Testing needs no subclass. Pass a fake formatter and assert on what run did. With Factory Method you must instantiate a real subclass or create a test-only fourth one.
  • Adding a format adds a formatter, not a formatter plus a job class.

What would make the subclass version correct: if the formats stop differing only in formatting.

  • PDF needs a page-break pass that the others do not, so run itself differs.
  • CSV can stream row by row while PDF must buffer the whole document, so the sequence differs, not just one step.
  • HTML needs an extra asset-upload step after the main upload.

Once two or three steps of the algorithm vary together and in a correlated way, you are no longer expressing a value — you are expressing a family of behaviours, and the base class holding the invariant sequence with several overridable steps is exactly Template Method with a Factory Method inside it.

The test I would apply: if every subclass body is one return new X(), it is a parameter. If the subclasses each override two or three different things that must agree with each other, it is a family.

Key takeaways

  • Three structures share the name: a simple factory (one function, one decision), Factory Method (a subclass chooses), and Abstract Factory (a consistent family).
  • A factory does not delete the conditional — it stops the conditional and the construction details from being copied to every call site.
  • Name the decision the factory makes. If you cannot, it is a rename.
  • Factory Method binds the choice by subclass, which is right for a family of behaviours and wrong for expressing a single value — inject the value instead.
  • Always have a default branch that throws. Unknown configuration should fail at start-up, not as a null reference three layers deep.
  • Call factories at the composition root, not inside business logic — a factory called deep in a service is still a hidden dependency.

Further reading

  • Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1994).
  • Joshua Bloch, Effective Java, third edition (Addison-Wesley, 2018) — the case for static factory methods over constructors.
  • Mark Seemann and Steven van Deursen, Dependency Injection Principles, Practices, and Patterns (Manning, 2019) — the composition root.
  • Martin Fowler, Patterns of Enterprise Application Architecture (Addison-Wesley, 2002).
  • Michael Feathers, Working Effectively with Legacy Code (Prentice Hall, 2004) — extracting a factory as a seam for testing.

Check yourself

3 questions in this lesson. Answer each once to complete it.

No problem practices this lesson yet. Browse the practice problems

Next lessonAbstract Factory
Lessons in this track