Factory Method Pattern in Java – With Real-World Examples and Best Practices

Illustration for Factory Method Pattern in Java – With Real-World Examples and Best Practices
By Last updated:

Introduction

The Factory Method Pattern is one of the most powerful and widely used creational design patterns in object-oriented programming. It provides an interface for creating objects in a superclass but allows subclasses to alter the type of objects that will be created.

Why Factory Method Matters

Instead of instantiating classes directly, the Factory Method delegates the responsibility of instantiation to subclasses, making your code flexible, extensible, and decoupled from concrete class implementations.

You’ve likely already used this pattern when working with frameworks like Spring, Java’s Calendar.getInstance(), or JDBC’s DriverManager.getConnection().


Core Intent and Participants

  • Intent: Define an interface for creating an object, but let subclasses decide which class to instantiate.
  • Participants:
    • Product: Common interface or abstract class for all concrete products.
    • ConcreteProduct: Specific implementation of the product.
    • Creator: Abstract class or interface declaring the factory method.
    • ConcreteCreator: Implements the factory method to return a specific ConcreteProduct.

UML (Textual)

+-------------------+          +------------------+
|   Creator         |<>--------|   Product        |
|-------------------|          |------------------|
| + factoryMethod() |          | + operation()    |
+-------------------+          +------------------+
        ^                             ^
        |                             |
+-------------------+      +---------------------+
| ConcreteCreator   |      |  ConcreteProduct    |
|-------------------|      |---------------------|
| + factoryMethod() |      | + operation()       |
+-------------------+      +---------------------+

Real-World Use Cases

  • Logging frameworks (LoggerFactory.getLogger())
  • GUI libraries creating OS-specific buttons (Windows vs Mac)
  • Java Collections (Collections.synchronizedList())
  • Database drivers (DriverManager.getConnection())
  • Parser factory in compilers (XML, JSON, CSV)

Implementation Strategies in Java

Step-by-Step Example

Step 1: Define the Product Interface

public interface Notification {
    void notifyUser();
}

Step 2: Create Concrete Implementations

public class EmailNotification implements Notification {
    public void notifyUser() {
        System.out.println("Sending an Email Notification");
    }
}

public class SMSNotification implements Notification {
    public void notifyUser() {
        System.out.println("Sending an SMS Notification");
    }
}

Step 3: Define the Creator (Factory)

public abstract class NotificationFactory {
    public abstract Notification createNotification();
}

Step 4: Implement Concrete Creators

public class EmailNotificationFactory extends NotificationFactory {
    public Notification createNotification() {
        return new EmailNotification();
    }
}

public class SMSNotificationFactory extends NotificationFactory {
    public Notification createNotification() {
        return new SMSNotification();
    }
}

Step 5: Client Code

public class FactoryMethodDemo {
    public static void main(String[] args) {
        NotificationFactory factory = new EmailNotificationFactory();
        Notification notification = factory.createNotification();
        notification.notifyUser();
    }
}

✅ Now you can change the notification type just by switching the factory.


Pros and Cons

✅ Pros

  • Promotes loose coupling
  • Supports Open/Closed Principle
  • Easy to introduce new product types

❌ Cons

  • Can increase the number of classes
  • Slightly more complex than direct instantiation

Anti-Patterns and Misuse

  • Over-abstracting simple object creation
  • Tight coupling between factory and specific products
  • Violating SRP by putting too much logic in the factory method

Factory vs Abstract Factory vs Builder

Pattern Purpose Product Count Complexity Example Use Case
Factory Method Create one product from a hierarchy One Low Logger, Notification
Abstract Factory Create families of related objects Many Medium UI Themes, OS widgets
Builder Step-by-step construction of complex objects One (Complex) Medium-High HTML, SQL Query, Car Configurator

Refactoring Legacy Code

Before (Tightly Coupled):

public class AlertService {
    private EmailNotification notification = new EmailNotification();

    public void sendAlert() {
        notification.notifyUser();
    }
}

After Applying Factory Method:

public class AlertService {
    private NotificationFactory factory;

    public AlertService(NotificationFactory factory) {
        this.factory = factory;
    }

    public void sendAlert() {
        Notification notification = factory.createNotification();
        notification.notifyUser();
    }
}

✅ Now you can inject different factories (Email, SMS, Push).


Best Practices

  • Return interfaces not concrete classes
  • Apply the pattern when object creation logic is complex
  • Name factories clearly (EmailNotificationFactory, not Factory1)
  • Consider using lambda or Supplier<T> in Java 8+

Real-World Analogy

Think of a Coffee Shop.

You don’t prepare your own coffee beans; you tell the barista (factory) you want a latte. The barista knows how to prepare it and serves you the final product.


Java Version Relevance

  • Java 8+: Use Supplier<T> to simplify factories:
Supplier<Notification> emailSupplier = EmailNotification::new;
Notification notification = emailSupplier.get();
  • Java 17+: Use sealed interfaces to limit products.

Conclusion & Key Takeaways

  • Factory Method Pattern decouples object creation from usage.
  • Ideal for cases where your app grows in functionality.
  • Encourages maintainability and extensibility.
  • Be careful of overuse for simple objects.

FAQ – Factory Method Pattern in Java

1. What is the Factory Method Pattern?

A creational pattern where object creation is delegated to a method in a subclass.

2. Is it a part of the GoF design patterns?

Yes, it’s one of the 23 original patterns from the GoF.

3. What's the main benefit of this pattern?

It decouples product creation from the client.

4. How is it different from Abstract Factory?

Factory Method creates a single product; Abstract Factory creates related families.

5. When should I use Factory Method?

When the exact type of object isn’t known until runtime.

6. Can we use lambdas as factories?

Yes, with Supplier<T> in Java 8+.

7. Is it better than using new directly?

Yes, especially when object creation logic is complex or repeated.

8. Does it violate SRP?

Not if the factory only handles creation logic.

9. Can I use DI with Factory Method?

Absolutely, especially in Spring-based apps.

10. What's a real-life analogy?

Ordering coffee from a barista instead of making it yourself.