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 the backend for 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.
Another branch in your logic.
Then, marketing wants to experiment with Slack alerts. Then WhatsApp.
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.
This becomes a nightmare to maintain:
Lets separate the “decision of what to create” from the logic of how to use it.
This is where the Simple Factory comes in.
A Simple Factory is not a formal design pattern in the Gang of Four (GoF) book, but it’s one of the most practical and widely-used refactoring techniques in real-world codebases.
Here’s the idea:
Let’s extract object creation into a separate class:
All creation logic is now in one place, and your notification sending logic becomes clean, readable, and open to extension.
And now NotificationService
is cleaner:
With this approach:
So you’ve cleaned things up with a Simple Factory. Your notification logic is now leaner and more modular. But as your product grows and you keep adding new notification types, something starts to feel off again.
Why?
Because your NotificationFactory
is beginning to look eerily similar to the bloated code you just refactored away from. Every time you introduce a new type (like Slack, WhatsApp, or In-App notifications), you’re right back to modifying the factory’s switch
or if-else
statements.
That’s not very Open/Closed, is it?
Your system is better, but it's still not open to extension without modification. You're still hardcoding the decision logic and centralizing creation in one place.
That’s when you realize you need to take things further. 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.
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.
By now, you’ve seen the shortcomings of bloated if-else
chains and central factories. You’ve seen how they become pain points as your system grows. With the Factory Method Design 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 break down what that actually looks like in the context of our notification system:
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.
Think of this class as a template:
This is key: the abstract creator defines the flow, not the details.
Now for the exciting part.
You create specific classes like
EmailNotificationCreator
SMSNotificationCreator
PushNotificationCreator
Each one extends the abstract creator and implements the createNotification()
method to return its specific notification type.
This is where the Factory Method magic happens:
new EmailNotification()
new SMSNotification()
new PushNotification()
No more conditionals. Each class knows what it needs to create, and the core system doesn’t need to care.
Now, if you want to send an email, you use EmailNotificationCreator
. For SMS, use SMSNotificationCreator
. The system knows the flow, and each subclass knows the details.
Here's how your app might use this architecture:
Each line:
send()
methodNotification
typeLet’s say you want to add Slack notifications.
With the old setup, you’d have to:
if-else
or switch
casesWith the Factory Method pattern, you simply:
SlackNotificationCreator
createNotification()
to return new SlackNotification()
Done. No modification to existing code. No regression risk. No coupling.
You can now use it like this: