StudyDSA logoStudyDSA

Command Palette

Search for a command to run...

Sign InSign Up
Sign Up

Data StructuresAlgorithmsBig-O NotationDesign PatternsSystem DesignMachine LearningPhysicsRoboticsAI Research

Factory MethodAbstract FactoryBuilderPrototypeSingletonAdapterBridgeCompositeDecoratorFacadeFlyweightProxyChain of ResponsibilityCommandIteratorMediatorMementoObserverStateStrategyTemplate MethodVisitor

Definition
StudyDSA

Where complexity meets clarity.
By Armas Zarra.

Topics

  • Data Structures
  • Algorithms
  • Big-O Notation
  • Robotics
  • AI Research
  • Machine Learning

Practice

  • Blind 75
  • LeetCode 75
  • NeetCode 150

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Armas Films LLC

Design PatternsFactory Method

Factory Method

A creational pattern where subclasses decide which class gets instantiated, so new product types plug in without touching the code that orders them.

Definition

Adding a third export format to a report tool should be one new class, but in most codebases it's an archaeology dig, because every function that builds an exporter carries its own copy of the if/else that picks one. Factory Method is the Gang of Four fix for exactly that. It's a creational pattern that moves object creation into a single overridable method, so subclasses decide which class gets instantiated and the surrounding logic never has to know. The official intent line from 1994 says it plainly: define an interface for creating an object, but let subclasses decide which class to instantiate.


Strip the ceremony away and a factory method is really just a virtual method that runs new. The base class calls it wherever it needs a fresh object, subclasses override it to change what comes out, and ordinary method dispatch does the rest. That one move turns "which class?" from a question every call site answers into a question answered exactly once.

The Duplicated Switch

Here's the smallest version of the problem Factory Method exists to kill. A report tool ships with two formats, PdfExporter and CsvExporter, and two functions need one: generate_report builds the full file while preview_report renders the first 12 characters. Watch what each function has to know:

The same four-line switch appears twice in one short file. Now picture the real version: a dozen entry points, each with its own copy, and a new JsonExporter on the roadmap. The compiler won't list the call sites for you, so you'll hunt them with grep, miss one, and ship a tool whose preview speaks a different format than its download button.

One Overridable Method

Factory Method rebuilds the report tool around one abstract method. The shared logic moves up into ReportApp, the switch disappears, and in its place sits create_exporter(), declared but deliberately left unimplemented. Read generate() closely: it creates an exporter and uses it, yet no concrete exporter is named anywhere in the class. The last two lines construct a CsvApp and call generate("q3 totals"). Before reading past the code, commit to a prediction: which exporter class runs, and what exactly gets printed?

The answer is q3,totals. The call lands in generate() on the base class, which calls self.create_exporter(), and dynamic dispatch routes that to the CsvApp override because that's the object's actual class. The override returns a CsvExporter, whose export() replaces the space in q3 totals with a comma. Hold a PdfApp instead and the same client line prints %PDF q3 totals. One override changed the product, and the base class never changed at all.

Who Does What

The Gang of Four gave the four roles in this arrangement names, and they're worth memorizing because every patterns conversation you'll ever have uses them. The interface everyone returns is the Product, which is Exporter here. The variants are ConcreteProducts, PdfExporter and CsvExporter. The class holding the shared logic plus the abstract creation hook is the Creator, our ReportApp, and each subclass that fills in the hook is a ConcreteCreator, like CsvApp.


The setup works like a franchise handbook. Corporate writes the operating manual once, leaves one page blank for the local specialty, and every location runs the same manual with its own page filled in. The analogy has a limit worth stating: a restaurant can change its specialty tomorrow, but a ConcreteCreator can't. The choice is welded in at construction time, so a CsvApp produces CSV exporters for as long as it lives. If you need the product to change call by call, you want an object you can swap at runtime, which is Strategy's territory rather than this pattern's.

Factory Method: Step-by-Step

Installing the pattern is four moves:

1. Give Every Product One Voice

List what all the variants can do, and that list becomes the Product interface. Here it's a single method: every exporter can export(data). The moment PdfExporter and CsvExporter both speak it, callers stop caring which one they're holding.

2. Move the Logic Up, Leave Creation Blank

The Creator (ReportApp) owns everything the variants share, like generate(), and declares one abstract method it refuses to implement. That hole is the factory method, and it's deliberate.

3. Fill the Blank Once Per Variant

Each ConcreteCreator is embarrassingly small: CsvApp overrides create_exporter() with a one-line return CsvExporter(). Adding a JSON format is a new exporter plus a three-line app class, and zero edits anywhere else.

4. Choose the Creator, Inherit the Choice

Client code makes exactly one decision: which app class to construct. From there, dynamic dispatch routes every create_exporter() call to the right override, and the base class's logic runs unchanged for every variant.
Without Inheritance: Go and Rust

There's an irony sitting in the book itself. The Gang of Four's most quoted advice is to favor object composition over class inheritance, yet Factory Method is one of the few patterns in their catalog built squarely on inheritance. Go forces the issue, since it has no subclassing at all. The Go tab in the code block above drops the creator class entirely: a factory is just a function value of type func() Exporter, and generate() accepts one as a parameter. That's the same seam with less machinery, and it matches how Go's standard library hands back interfaces from constructor functions instead of exposing concrete structs.


Rust gets closer to the original shape without inheriting anything. A trait can carry finished logic in a provided method, so generate() lives in the ReportApp trait body while create_exporter() stays required, and every implementing type fills in that one blank. The returned Box<dyn Exporter> is the runtime-choice version, and when the product type is known at compile time, generics do the same job without the allocation. The pattern survives losing inheritance because the load-bearing part is the overridable seam, not the class diagram.

Three Things Called Factory

You might think every helper with create in its name is this pattern. Most of them are what folklore calls a simple factory: one standalone function, one switch, all creation funneled through it. It's useful and it's everywhere, but it isn't in the Gang of Four book. Factory Method specifically means the inheritance arrangement traced above, where a creator's finished logic calls a hook that subclasses override. If nothing inherits and nothing overrides, you're looking at a simple factory wearing the pattern's name tag.


The other mix-up is Abstract Factory, and the names do not help. Factory Method is one method that varies one product. Abstract Factory is a whole object whose interface bundles several creation methods for a family that has to match, like a dark theme that must produce a dark button, a dark menu, and a dark dialog together. The memory hook: Factory Method picks which product, Abstract Factory picks which family. The two also nest, because an abstract factory's individual methods are often implemented as factory methods, which is exactly why people conflate them.

When It's Overkill

Java's enterprise era turned this pattern into a punchline. Steve Yegge's Execution in the Kingdom of Nouns skewered codebases where no verb may act without a noun escort, and the FactoryFactory jokes write themselves once factories start constructing other factories. The ridicule has a real target: a creator hierarchy wrapped around a class with one product and no second in sight is pure ceremony, three files where one new would do.


Reach for the pattern when two things are true at once: the creation step varies by type, and the logic around it shouldn't. In a language with first-class functions, check whether passing a closure solves it first, because handing generate() a creation function as a plain argument is this entire page compressed into one parameter. The class hierarchy earns its keep when creators carry real shared behavior, not just a wrapped constructor.

Best Practices

The pattern is small, and these five details decide whether it pays for itself:


The factory method's declared return type is Exporter, and that's a promise to callers. Declare it as PdfExporter and someone will reach for a PDF-only method, and the decoupling you built the pattern for quietly dies.

Check Yourself

Two questions before you go. First: generate() is finished code on the base class and never gets overridden, yet different apps get different exporters out of it. What mechanism picks which create_exporter() body runs? Second: a teammate writes one static make_exporter(kind) helper with an if/else inside and calls it the Factory Method pattern. What did they actually build, and when is their version the smarter choice? Both answers are on this page, and if either feels shaky, reread the trace in One Overridable Method.


Then go spot it in the wild, because you've been calling factory methods for years. Java's iterator() is one: every collection decides which iterator class you actually get, and your for-each loop never knew. Calendar.getInstance() picks a calendar subclass based on your locale, though purists will note it's a static factory, the cousin that skips subclassing. Last, open a codebase you work on and find the if/else that picks a class. When you spot it copied into a second function, you've found tomorrow's create_exporter().

the_duplicated_switch.py

class PdfExporter:
    def export(self, data: str) -> str:
        return "%PDF " + data

class CsvExporter:
    def export(self, data: str) -> str:
        return data.replace(" ", ",")

def generate_report(kind: str, data: str) -> str:
    # The switch that creeps into every function needing an exporter
    if kind == "pdf":
        exporter = PdfExporter()
    else:
        exporter = CsvExporter()
    return exporter.export(data)

def preview_report(kind: str, data: str) -> str:
    # The same switch again: a new format now means editing both copies
    if kind == "pdf":
        exporter = PdfExporter()
    else:
        exporter = CsvExporter()
    return exporter.export(data[:12])

factory_method.py

from abc import ABC, abstractmethod

class Exporter(ABC):  # Product: the interface every export format speaks
    @abstractmethod
    def export(self, data: str) -> str: ...

class PdfExporter(Exporter):  # ConcreteProduct
    def export(self, data: str) -> str:
        return "%PDF " + data

class CsvExporter(Exporter):  # ConcreteProduct
    def export(self, data: str) -> str:
        return data.replace(" ", ",")

class ReportApp(ABC):  # Creator: owns the logic, leaves creation blank
    @abstractmethod
    def create_exporter(self) -> Exporter: ...  # the factory method

    def generate(self, data: str) -> str:
        # Works against the Exporter interface, never a concrete class
        exporter = self.create_exporter()
        return exporter.export(data)

class PdfApp(ReportApp):  # ConcreteCreator: fills in the one blank
    def create_exporter(self) -> Exporter:
        return PdfExporter()

class CsvApp(ReportApp):  # ConcreteCreator
    def create_exporter(self) -> Exporter:
        return CsvExporter()

# Example usage
app: ReportApp = CsvApp()
print(app.generate("q3 totals"))  # q3,totals