Design Pattern — Factory Method
How subclasses take over object creation — and when to let them
A factory exists for one reason: to take the “how do I build this” decision away from the people who just need the finished product. Factory Method does the same in code. It takes the choice of which class to create out of the code that uses the object, and hands that choice to a subclass.
This post covers what Factory Method is, how to build it in Java, and when it helps versus when it’s overkill. It does not cover Abstract Factory — that one handles families of related objects, and it gets its own post.
What is the Factory Method Pattern?
Picture this. Your order service processes a purchase and needs to notify the customer. You start with email, so you write new EmailNotification() right inside the order logic. It works. Six months later, the product team wants SMS for urgent orders and push notifications for mobile users. Now you edit the order service — a class whose job is processing orders, not building notifications — every time a channel is added. And the same coupling spreads to every other class that sends notifications.
Factory Method fixes this. You add a separate notification-sending class — the Creator — with a createNotification() method. The order service asks the Creator for a notification and uses whatever it gets back. It never names EmailNotification or SmsNotification directly — only Notification. Each Creator subclass (one for email, one for SMS) overrides createNotification() to return its own type. The calling code stays the same no matter how many types exist.
Think of a hiring process. HR defines the workflow — post the role, screen candidates, interview, make an offer. But each department decides who to hire. Engineering hires engineers, Marketing hires marketers, Legal hires lawyers. HR never says “hire this exact person.” It says “fill the role,” and each department does that its own way. HR is the Creator, each department a ConcreteCreator, and the person hired is the Product.
“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.” — Gang of Four (GoF), Design Patterns: Elements of Reusable Object-Oriented Software
Here’s the key insight. The code that calls the factory method never names a concrete class. It works only through the Product interface. So the painful scenario — hunting down every new EmailNotification() and every else if each time a channel appears — simply can't happen. The choice of concrete type lives in exactly one place.
Examples
Real-world systems that use Factory Method —
Collection.iterator()in Java — EveryCollectionsubclass (ArrayList,LinkedList,HashSet) overridesiterator()to create its ownIterator. You calliterator()and get back anIterator, never knowing which concrete class you're iterating with. This is the textbook case — the GoF book itself uses iterator creation as a Factory Method.URLStreamHandler.openConnection()in Java —URL.openConnection()hands off to a protocol-specific handler subclass. The HTTP handler overridesopenConnection()to build anHttpURLConnection; FTP builds its own. The Creator is the handler, the factory method isopenConnection(), and the caller uses the returnedURLConnectionwithout ever naming the concrete type.- Spring’s
FactoryBean<T>— Implementations overridegetObject()to build beans that are hard to construct with a plain constructor. The container callsgetObject()and wires the result, so your configuration never names the concrete product class. It's a looser fit than the first two — the container, not the Creator's own business logic, consumes the product — but the override-to-decide-the-type mechanism is the same.
One honest caveat, because the term gets used loosely. A pluggable LoggerFactory or a registered URLStreamHandlerFactory is closer to a simple factory or Abstract Factory. There, the factory is an object you pass in (composition), not a method a subclass overrides (inheritance). The true GoF Factory Method lives mostly in frameworks and in your own extensible code.
I used Factory Method in a radiology imaging pipeline. The processing steps were the same across scans, but each modality (CT, MRI, X-ray) needed its own decoder. Each ScanProcessor subclass built its decoder through a factory method. Adding ultrasound later took one subclass, and the pipeline held.
Problems Addressed by Factory Method
- Hardcoded
newties your code to concrete classes — Writingnew EmailNotification()locks business logic to that class. If its constructor changes or the class gets replaced, every call site breaks. - Adding a new type forces changes everywhere — Without the pattern, every place that creates a product needs editing when a new type appears. With Factory Method, you add one Creator and one Product. Existing code stays untouched.
- Creation logic gets duplicated — When several classes build the same object with the same setup, that setup gets copy-pasted. Factory Method puts it in one place.
- Testing is harder than it should be — If business logic calls
new ConcreteProduct()directly, you can't swap in test doubles without touching production code. With the pattern, you override the factory method in a test subclass. - The class that uses the object shouldn’t pick the type — An order service should know “I need a notification,” not whether it’s email, SMS, or push.
The Problem — Hardcoded Object Creation
Here’s notification code without the pattern:
public class OrderService {
public void processOrder(Order order) {
// #1 — Business logic
saveOrder(order);
chargePayment(order);
// #2 — Notification creation hardcoded into business logic
if (order.getChannel().equals("EMAIL")) {
EmailNotification email = new EmailNotification();
email.send("Order " + order.getId() + " confirmed");
} else if (order.getChannel().equals("SMS")) {
SmsNotification sms = new SmsNotification();
sms.send("Order " + order.getId() + " confirmed");
} else if (order.getChannel().equals("PUSH")) {
PushNotification push = new PushNotification();
push.send("Order " + order.getId() + " confirmed");
}
}
}- #1 — Saving and charging are the real job of
OrderService. - #2 —
OrderServicedepends directly on three concrete notification classes. Every new channel means anotherelse ifhere — and in every other class that sends notifications.
The order service should ask for a notification and use whatever it gets — not decide how to build one. That’s the coupling Factory Method removes.
UML for Factory Method
Runtime Flow
Implementing Factory Method
Step 1 — Product interface
Define what every notification can do, without specifying how.
// #1 — Product interface
public interface Notification {
void send(String message); // #2 — Every notification type implements this differently
}- #1 — All code that consumes notifications depends on this interface, not on any concrete class.
- #2 — Each implementation sends differently — SMTP for email, an API call for SMS, a push service for mobile.
Step 2 — Concrete Products
Each notification type implements the interface with its own logic.
// #1 — Email notification
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending email: " + message);
}
}
// #2 — SMS notification
public class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}- #1, #2 — Each product is self-contained. Email knows about email delivery. SMS knows about SMS gateways. Neither is referenced outside its own ConcreteCreator.
Step 3 — Abstract Creator
// #1 — Abstract Creator
public abstract class NotificationService {
// #2 — The factory method (subclasses override this)
public abstract Notification createNotification();
// #3 — Workflow that uses the factory method
public void sendNotification(String message) {
Notification notification = createNotification(); // #4
notification.send(message);
}
}- #2 — The factory method. It’s abstract — the decision of which
Notificationto build belongs to subclasses. - #3 —
sendNotification()is the business logic. Identical for every notification channel. - #4 — The Creator gets back a
Notificationand uses it through the interface. It never sees the concrete class.
Step 4 — Concrete Creators
Each subclass overrides the factory method to return its specific product.
// #1 — Creates EmailNotification
public class EmailNotificationService extends NotificationService {
@Override
public Notification createNotification() {
return new EmailNotification(); // #2
}
}
// #3 — Creates SmsNotification
public class SmsNotificationService extends NotificationService {
@Override
public Notification createNotification() {
return new SmsNotification(); // #4
}
}- #2, #4 — This is the only place in the entire codebase where a concrete notification class is named. Everything else works through the
Notificationinterface. - #1, #3 — Adding push notifications means writing
PushNotificationandPushNotificationService. No existing class changes.
Step 5 — Usage
The “before” code switched on order.getChannel() at runtime, so the channel is data, not a compile-time choice. That selection still happens somewhere — but it shrinks to a single registry lookup instead of an if/else chain.
// #1 — Register each channel once, at startup
Map<String, NotificationService> registry = Map.of(
"EMAIL", new EmailNotificationService(),
"SMS", new SmsNotificationService(),
"PUSH", new PushNotificationService()
);
// #2 — OrderService looks up the Creator by channel — no branching
NotificationService service = registry.get(order.getChannel());
service.sendNotification("Order " + order.getId() + " confirmed"); // #3- #1 — The channel-to-Creator mapping lives in one place, built at startup — usually through dependency injection (passing dependencies into a class instead of having it create them) or a config file. Dependency injection is a big topic on its own; I’ll cover it in a future post.
- #2 — The
OrderServicenow looks up aNotificationServiceand calls it. The runtimeif/elseis gone — adding a channel means one new registry entry, not another branch. - #3 — The call is the same regardless of notification type. No conditionals, no concrete product references in the business logic.
When to Use Factory Method
- The concrete type isn’t known at compile time — The product depends on configuration, user input, or runtime conditions. Factory Method lets the right subclass create it, so the caller doesn’t have to decide.
- Creation logic should be kept apart from usage — When construction involves heavy setup — connection pools, auth tokens, retry policies — the business logic shouldn’t carry it. A Creator subclass does.
- You want to reuse objects instead of rebuilding them — A factory method doesn’t have to return a fresh instance. It can hand back a cached or pooled object — something a plain constructor can never do, since a constructor must always return a new one. This is why expensive resources like database connections are often handed out through a factory method.
- You’re building a framework or library — You define the workflow; the consumer provides the concrete types. Factory Method gives them a clean extension point — the way
Collection.iterator()lets each collection supply its own iterator. - Testing needs swappable implementations — Override the factory method in a test subclass to return mocks, stubs, or in-memory fakes, without touching production code.
You don’t need Factory Method when there’s one concrete type and no real chance of more. If your system will only ever send email, new EmailNotification() is simpler and honest. The pattern earns its keep when variation exists or is clearly coming.
Factory Method vs. Abstract Factory
These two are often confused — both involve creation, both use the word “factory.” The difference is scope.
- Factory Method uses inheritance. A Creator class declares a factory method. Subclasses override it to produce one product. The Creator’s workflow uses the product but doesn’t know its concrete type.
- Abstract Factory uses composition. A factory object is passed into the client, and it creates families of related objects — a button, a checkbox, and a scrollbar that all match the same theme. The factory is a collaborator, not a superclass.
If you’re creating a single product and the only question is which subclass creates it — Factory Method. If you need related objects created together as a set (a Windows button paired with a Windows checkbox) — Abstract Factory, covered in its own post.
Issues with Factory Method
Class proliferation — Every new product type needs two new classes: a ConcreteProduct and a ConcreteCreator. Ten channels means twenty new classes, plus the interface and abstract class. The structure is clean, but the file count grows fast. Inheritance dependency — Factory Method relies on subclassing. If your Creator already sits in a deep hierarchy, another layer adds complexity. Java’s single inheritance makes this a real constraint — the Creator must be the one class you choose to extend.
Get Vivek Mittal’s stories in your inbox
Join Medium for free to get updates from this writer.
Overkill for stable, simple cases — With two product types that won’t change, a conditional or a static factory method (a static method that returns the right type from a parameter) is more readable. Use Factory Method when variation is real or growing — not as insurance against change that may never come.
Indirection complicates debugging — When a created object misbehaves, the stack trace passes through the abstract creator, the override, and the concrete product. Newcomers need a few passes before it clicks.
The decision still lives somewhere — Factory Method moves new ConcreteProduct() out of business logic, but someone still picks which Creator to use — a registry, configuration, or DI. If that layer grows its own sprawling conditional, you've moved the problem, not solved it.
Summary
- Factory Method is a creational pattern (one of the five GoF patterns dealing with object creation) that defers creation to subclasses, keeping concrete class references out of business logic
- It has two parallel hierarchies: the Creator (the workflow and the factory method) and the Product (the interface for the objects being created)
- The calling code works through the Product interface — it never names or depends on a concrete class
- Adding a new product type means one ConcreteProduct and one ConcreteCreator. No existing code changes
- The trade-off is more classes and more indirection in exchange for extensibility and separation of concerns
- Use it when product types vary or will grow. Skip it when there’s one stable type and no real chance of more
Conclusion
Factory Method does one thing well: it moves the decision of which class to create into a place where it can change on its own, apart from the code that uses the instance. The business logic calls a method, gets a product, and works with it through an interface — never knowing the concrete type.
The pattern isn’t free. You pay with more classes, deeper inheritance, and a longer stack trace when debugging. For one product type that won’t change, it’s overhead. But where types grow over time, Factory Method keeps the consuming code from noticing.
The clearest sign you need it: you’re editing a class that uses an object just because the type of that object changed. Factory Method makes that edit unnecessary. If you’ve followed this series, you’ve seen Singleton, Builder, and Prototype — all creational, all solving different facets of how objects come into existence. Factory Method rounds out the set by answering who decides.
Written & edited by — Vivek Mittal








