Last Updated: February 21, 2026
The Factory Method Design Pattern is a creational pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
It’s particularly useful in situations where:
When you have multiple objects of similar type, you might start with basic conditional logic (like if-else or switch statements) to decide which object to create.
But as your application grows, this approach becomes rigid, harder to test, and tightly couples your code to specific classes, violating key design principles.
Factory method lets you create different objects without tightly coupling your code to specific classes.
Let’s walk through a real-world example to see how we can apply the Factory Method Pattern to build a more scalable and maintainable object creation workflow.
Imagine you're building a web application that sends notifications to users. At first, it’s simple. You're only sending email notifications.
A single class takes care of that.
To use it in our service, we create the email notification object and call the send() method.
All good. But then comes a new requirement: support SMS notifications.
So, you add a new class and update your NotificationService class by adding a new if block to create an SMS notification object, and send that too.
Slightly more complex, but still manageable. A few weeks later, product wants to send push notifications to mobile devices. Then marketing wants Slack alerts. Then WhatsApp.
Each one adds another branch:
Now, your notification code is starting to look like a giant control tower. It’s responsible for creating every kind of notification, knowing how each one works, and deciding which to send based on the type.
Here is what the dependency structure looks like:
Adding a sixth notification type means modifying NotificationService yet again.
This becomes a nightmare to maintain:
Before jumping to the full Factory Method pattern, there is a common intermediate step: extract the creation logic into a separate class.
This is called the Simple Factory. It is not a formal Gang of Four (GoF) pattern, but it is one of the most practical refactoring techniques in real-world codebases.
The idea is straightforward: create a separate class whose only job is to centralize and encapsulate object creation. The notification service no longer needs to know which concrete class to instantiate. It asks the factory.
All creation logic is now in one place. Now NotificationService is cleaner:
This is better. The service only uses the notification, it does not construct it. Adding new types is easier since you only modify the factory, not every service that uses notifications.
But as your product grows and you keep adding new notification types, something starts to feel off again. Your SimpleNotificationFactory is beginning to look eerily similar to the bloated code you just refactored away from.
Every time you introduce a new type, you are right back to modifying the factory's switch or if-else statements.
That is not very Open/Closed, is it?
Your system is better, but it is still not open to extension without modification. You are still hardcoding the decision logic and centralizing creation in one place. You need to give each type of notification its own responsibility for knowing how to create itself.
And that’s exactly the type of problems Factory Method Design Pattern solves.
The Factory Method Pattern takes the idea of object creation and hands it off to subclasses. Instead of one central factory deciding what to create, you delegate the responsibility to specialized classes that know exactly what they need to produce.
In simpler terms:
So now, instead of having:
You have:
Your creation logic is decentralized.
Think of a food delivery platform. You place an order. If the system is designed like a Simple Factory, there’s one centralized kitchen deciding whether to cook pizza, sushi, or burgers.
But with the Factory Method, each restaurant (Pizza Place, Sushi Bar, Burger Joint) has its own kitchen and knows how to prepare its food. The platform just asks the appropriate kitchen to handle it.
The interface or abstract class that defines the contract for all objects the factory method creates. Every concrete product implements this interface, which means the rest of the system can work with any product without knowing its concrete type.
In our notification example, this is the Notification interface with its send() method. The creator and client code only ever reference this interface, never EmailNotification or SMSNotification directly.
The actual classes that implement the Product interface. Each one provides its own behavior. EmailNotification connects to an SMTP server. SMSNotification calls a telephony API. PushNotification talks to Firebase or APNs.
They all share the same send() method signature but do completely different things internally.
An abstract class (or an interface) that declares the factory method, which returns an object of type Product.
The Creator does two things:
createNotification()) that subclasses must implement.send() method in our NotificationCreator calls createNotification() to get a product, then uses it. The Creator defines the workflow, the subclasses fill in the details.Subclasses of Creator that override the factory method to return a specific ConcreteProduct. Each creator is paired with exactly one product type.
EmailNotificationCreator returns new EmailNotification().SMSNotificationCreator returns new SMSNotification()Here is the Factory Method workflow, step by step:
The client code decides which ConcreteCreator to use based on configuration, user input, or business logic. For example, if the user wants an email notification, the client instantiates EmailNotificationCreator.
The client calls send() (or whatever the high-level operation is). This method lives in the abstract Creator class.
Inside send(), the Creator calls createNotification(). Since the Creator is abstract, this call is dispatched to the ConcreteCreator's override.
Step 4: ConcreteCreator returns a ConcreteProduct
EmailNotificationCreator.createNotification() returns a new EmailNotification(). The Creator receives it as the Notification interface type.
The Creator calls notification.send(message) on the product it just received. The correct concrete behavior executes.
By now, you have seen the shortcomings of bloated if-else chains and central factories.
With the Factory Method pattern, we flip that around. Instead of putting the burden of decision-making in a single place, we distribute object creation responsibilities across the system in a clean, organized way.
Let's implement it step by step.
This is the contract that all notification types must follow. Any code that works with notifications only depends on this interface.
Each notification type implements the interface with its own behavior.
We create an abstract class that declares the factory method createNotification(), and optionally includes shared behavior like send()that defines the high-level logic of sending a notification by using whatever object createNotification() provides.
The Creator defines the flow, subclasses fill in the details.
Think of this class as a template: it does not know what notification it is sending, but it knows how to send it. It defers the choice of notification type to its subclasses.
The abstract creator defines the flow, not the details.
Now for the part that makes the pattern work. Each concrete creator extends the abstract creator and overrides the factory method to return its specific product.
No more conditionals. Each class knows what it needs to create, and the core system does not need to care.
EmailNotificationCreator returns new EmailNotification()SMSNotificationCreator returns new SMSNotification()The mapping is one-to-one and explicit.
Here's how your application might use this architecture:
Each line creates the appropriate creator, calls the shared send() method, and the right notification type is created and used internally. The client never touches concrete product classes directly.
This is where the pattern pays off. Say you want to add WhatsApp notifications. With the old approach, you would modify an existing factory or service class, add a new if-else branch, and risk breaking existing logic.
With Factory Method, you simply create two new classes:
Done. No modification to existing code. No regression risk. No coupling. You just add new files and use the new creator wherever needed:
Let's build a complete example in a different domain to show the pattern's versatility. We will create a document export system that generates reports in multiple formats: PDF, HTML, and CSV.
A reporting service needs to export data in different formats. Each format has its own rendering logic, headers, and file structure. New formats (Markdown, XML, Excel) might be added in the future.
export() method in the Creator defines the common sequence (header, rows, footer) once.