DEV Community

Cover image for Demystifying the Factory Method Design Pattern in Java
Lathika Sri
Lathika Sri

Posted on

Demystifying the Factory Method Design Pattern in Java

Introduction

In software development, creating objects can become complicated when a program needs to work with different types of objects. If object creation logic is written directly throughout the application, the code can become tightly coupled and difficult to maintain.

The Factory Method Design Pattern provides a solution to this problem. It is a creational design pattern that defines a method for creating objects while allowing subclasses to decide which specific object should be created.

Instead of directly creating an object using the new keyword everywhere, the Factory Method delegates the responsibility of object creation to a separate method.


What is the Factory Method Pattern?

The Factory Method Pattern is a design pattern in which a superclass defines a method for creating an object, but the subclass determines the actual type of object that will be created.

The main idea can be expressed as:

Let subclasses decide which object to create.

For example, imagine a notification system that can send:

  • Email notifications
  • SMS notifications
  • Push notifications

Without a Factory Method, the application may contain many if-else or switch statements for deciding which notification object to create.

With the Factory Method, the object creation process is separated from the code that uses the object.


The Problem Without Factory Method

Consider a simple notification application.

class EmailNotification {
    void send() {
        System.out.println("Sending Email");
    }
}

class SMSNotification {
    void send() {
        System.out.println("Sending SMS");
    }
}
Enter fullscreen mode Exit fullscreen mode

Suppose the application directly creates objects:

public class NotificationService {

    public void sendNotification(String type) {

        if (type.equals("email")) {
            EmailNotification email = new EmailNotification();
            email.send();

        } else if (type.equals("sms")) {
            SMSNotification sms = new SMSNotification();
            sms.send();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This approach works, but there is a problem.

If we later add:

WhatsAppNotification
PushNotification
TelegramNotification
Enter fullscreen mode Exit fullscreen mode

the NotificationService class must be modified every time.

This creates tight coupling between the service and the concrete notification classes.


Solution Using Factory Method

The Factory Method solves this problem by introducing a common interface.

Step 1: Create the Product Interface

interface Notification {
    void send();
}
Enter fullscreen mode Exit fullscreen mode

This represents the common behavior of all notifications.


Step 2: Create Concrete Products

class EmailNotification implements Notification {

    public void send() {
        System.out.println("Sending Email");
    }
}
Enter fullscreen mode Exit fullscreen mode
class SMSNotification implements Notification {

    public void send() {
        System.out.println("Sending SMS");
    }
}
Enter fullscreen mode Exit fullscreen mode

Both classes implement the Notification interface.


Step 3: Create the Creator

abstract class NotificationCreator {

    abstract Notification createNotification();

    public void sendNotification() {
        Notification notification = createNotification();
        notification.send();
    }
}
Enter fullscreen mode Exit fullscreen mode

The createNotification() method is the Factory Method.

The creator does not know which concrete notification will be created.


Step 4: Create Concrete Creators

class EmailNotificationCreator extends NotificationCreator {

    @Override
    Notification createNotification() {
        return new EmailNotification();
    }
}
Enter fullscreen mode Exit fullscreen mode
class SMSNotificationCreator extends NotificationCreator {

    @Override
    Notification createNotification() {
        return new SMSNotification();
    }
}
Enter fullscreen mode Exit fullscreen mode

Each subclass decides which object should be created.


Step 5: Use the Factory Method

public class Main {

    public static void main(String[] args) {

        NotificationCreator creator;

        creator = new EmailNotificationCreator();
        creator.sendNotification();

        creator = new SMSNotificationCreator();
        creator.sendNotification();
    }
}
Enter fullscreen mode Exit fullscreen mode

Output

Sending Email
Sending SMS
Enter fullscreen mode Exit fullscreen mode

How Does Factory Method Work?

The pattern can be understood through four main components:

             Creator
                |
       createNotification()
                |
        ----------------
        |              |
 EmailCreator     SMSCreator
        |              |
        ↓              ↓
 EmailNotification  SMSNotification
Enter fullscreen mode Exit fullscreen mode

1. Product

Defines the common interface for the objects.

Example:

interface Notification
Enter fullscreen mode Exit fullscreen mode

2. Concrete Product

Implements the product interface.

Examples:

EmailNotification
SMSNotification
Enter fullscreen mode Exit fullscreen mode

3. Creator

Declares the Factory Method.

Example:

abstract class NotificationCreator
Enter fullscreen mode Exit fullscreen mode

4. Concrete Creator

Overrides the Factory Method and decides which concrete product to create.

Examples:

EmailNotificationCreator
SMSNotificationCreator
Enter fullscreen mode Exit fullscreen mode

Real-World Example

Consider an online food delivery application.

The application may support different payment methods:

Payment
   |
   ├── CreditCardPayment
   ├── UPIPayment
   └── CashPayment
Enter fullscreen mode Exit fullscreen mode

Instead of making the main application directly create each payment object, the Factory Method can delegate object creation to specialized creators.

This makes it easier to add another payment method later without heavily modifying existing code.

For example:

UPI
Credit Card
Debit Card
Cash
Digital Wallet
Enter fullscreen mode Exit fullscreen mode

The same concept can be used in:

  • Database connection creation
  • Notification systems
  • Document creation
  • Payment systems
  • Vehicle manufacturing systems
  • File parsers
  • Logging systems
  • Cloud service providers

Factory Method vs Simple Factory

These two concepts are often confused.

Simple Factory

A single class decides which object to create.

if (type.equals("email"))
    return new EmailNotification();

if (type.equals("sms"))
    return new SMSNotification();
Enter fullscreen mode Exit fullscreen mode

The factory contains the decision-making logic.

Factory Method

The decision is delegated to subclasses.

NotificationCreator
       |
       +--- EmailCreator
       |
       +--- SMSCreator
Enter fullscreen mode Exit fullscreen mode

Therefore, Factory Method provides greater flexibility and supports extension through inheritance.


Advantages of Factory Method

1. Loose Coupling

The client does not need to depend directly on concrete classes.

2. Easy Extension

New product types can be added with minimal changes to existing code.

3. Supports the Open/Closed Principle

The system can be extended without constantly modifying existing code.

4. Centralizes Object Creation

Object creation is separated from the business logic that uses the objects.

5. Improves Maintainability

As applications grow, separating object creation can make the code easier to understand and maintain.


Disadvantages

1. More Classes

Factory Method can introduce additional creator and product classes.

2. Increased Complexity

For very small applications, using a Factory Method may be unnecessary.

3. Inheritance Dependency

The traditional Factory Method relies on subclasses to determine object creation.


When Should You Use Factory Method?

Factory Method is useful when:

  • The exact type of object is not known until runtime.
  • A system needs to create multiple related types of objects.
  • Object creation logic is becoming complicated.
  • You want to reduce coupling between the client and concrete classes.
  • New object types are expected to be added in the future.

However, a design pattern should not be used simply because it exists. For a small application with only one or two object types, a Factory Method may introduce unnecessary complexity.


Conclusion

The Factory Method Design Pattern is a powerful creational pattern that separates object creation from the code that uses those objects.

Instead of allowing the client to directly create concrete objects, the Factory Method provides a controlled mechanism for creating them.

The key idea is:

Client
  ↓
Creator
  ↓
Factory Method
  ↓
Concrete Product
Enter fullscreen mode Exit fullscreen mode

By using this approach, software can become more loosely coupled, extensible, and maintainable.

Factory Method is particularly useful in large applications where new object types may be introduced frequently. Understanding this pattern also provides a strong foundation for learning other creational patterns such as Abstract Factory, Builder, Singleton, and Prototype.

Top comments (0)