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
, notFactory1
) - 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.