AlgoMaster Logo

Factory Method Design Pattern

Ashish

Ashish Pratap Singh

8 min read

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:

  • The exact type of object to be created isn't known until runtime.
  • Object creation logic is complexrepetitive, or needs encapsulation.
  • You want to follow the Open/Closed Principle, open for extension, closed for modification.

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.

1. The Problem: Sending Notifications

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 notificationknowing how each one works, and deciding which to send based on the type.

This becomes a nightmare to maintain:

  • Every time you add a new notification channel, you must modify the same core logic.
  • Testing becomes cumbersome because the logic is intertwined with object creation.
  • It violates key design principles, especially the Open/Closed Principle—the idea that classes should be open for extension but closed for modification.

2. Clean It Up with a Simple Factory

Lets separate the “decision of what to create” from the logic of how to use it.

This is where the Simple Factory comes in.

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:

  • You create a separate class (a “factory”) whose only job is to centralize and encapsulate object creation.
  • The notification service no longer needs to know which concrete class to instantiate. It simply asks the factory for the right type of notification.

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:

  • Your core logic becomes more focused. It only uses the notification, it doesn't construct it.
  • Adding new notification types becomes easier. You just modify the factory, not the service using it.
  • Testing and maintenance get simpler.

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.

3. What is Factory Method

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:

  • Each subclass defines its own way of instantiating an object.
  • The base class defines a common interface for creating that object, but doesn’t know what the object is.
  • The base class also often defines common behavior, using the created object in some way.

So now, instead of having:

You have:

Your creation logic is decentralized.

Real-World Analogy

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.

Class Diagram

  1. Product (e.g., Notification): An interface or abstract class for the objects the factory method creates.
  2. ConcreteProduct (e.g., EmailNotification, SMSNotification): Concrete classes that implement the Product interface.
  3. Creator (e.g., NotificationCreator): An abstract class (or an interface) that declares the factory method, which returns an object of type Product. It might also define a default implementation of the factory method. The Creator can also have other methods that use the product created by the factory method.
  4. ConcreteCreator (e.g., EmailNotificationCreator, SMSNotificationCreator): Subclasses that override the factory method to return an instance of a specific ConcreteProduct.

4. Implementing Factory Method

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:

1. Define the Product Interface

2. Define Concrete Products

3. Define an Abstract Creator

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:

  • It doesn't know what notification it's sending but it knows how to send it.
  • It defers the choice of notification type to its subclasses.

This is key: the abstract creator defines the flow, not the details.

4. Define Concrete Creators

Now for the exciting part.

You create specific classes like 

  • EmailNotificationCreator
  • SMSNotificationCreator
  • PushNotificationCreator
  • ...and so on

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:

  • EmailNotificationCreator → returns new EmailNotification()
  • SMSNotificationCreator → returns new SMSNotification()
  • PushNotificationCreator → returns 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.

5. Using it in the Client Code

Here's how your app might use this architecture:

Each line:

  • Creates the appropriate creator
  • Calls the shared send() method
  • Internally delegates to the right Notification type

6. Easy Addition

Let’s say you want to add Slack notifications.

With the old setup, you’d have to:

  • Modify your factory
  • Add new if-else or switch cases
  • Risk breaking existing logic

With the Factory Method pattern, you simply:

  • Create a new class SlackNotificationCreator
  • Implement createNotification() to return new SlackNotification()

Done. No modification to existing code. No regression risk. No coupling.

You can now use it like this: