Factory Method Design Pattern in Java
Introduction
The Factory Method is one of the most widely used creational design patterns. Its purpose is simple yet powerful: define an interface for creating an object, but let subclasses decide which class to instantiate.
Instead of hardcoding object creation using the new keyword, the Factory Method pattern delegates this responsibility to subclasses or dedicated factory classes. This promotes flexibility and loose coupling — two qualities every scalable application needs.
Problem Statement
Imagine you are building a document processing system that supports multiple file formats: Word, PDF, and Excel.
A naïve approach might look like this:
public class DocumentApp {
public static void main(String[] args) {
String type = "PDF";
if (type.equals("WORD")) {
WordDocument doc = new WordDocument();
doc.open();
} else if (type.equals("PDF")) {
PdfDocument doc = new PdfDocument();
doc.open();
} else if (type.equals("EXCEL")) {
ExcelDocument doc = new ExcelDocument();
doc.open();
}
}
}
This works, but there’s a big problem:
- Every time a new document type is added, you must modify the if-else chain.
- The DocumentApp class is tightly coupled to concrete document classes.
- Testing and extending the system becomes painful.
Solution with Factory Method
The Factory Method pattern solves this by introducing a common interface for documents and delegating object creation to a factory.
- Clients work only with the Document interface.
- Concrete factories decide which document to create.
- Adding a new document type means creating a new factory, without touching client code.
Implementation in Java
Step 1: Create the Product Interface
public interface Document {
void open();
void save();
}
Step 2: Concrete Products
public class WordDocument implements Document {
@Override
public void open() {
System.out.println("Opening Word Document...");
}
@Override
public void save() {
System.out.println("Saving Word Document...");
}
}
public class PdfDocument implements Document {
@Override
public void open() {
System.out.println("Opening PDF Document...");
}
@Override
public void save() {
System.out.println("Saving PDF Document...");
}
}
public class ExcelDocument implements Document {
@Override
public void open() {
System.out.println("Opening Excel Document...");
}
@Override
public void save() {
System.out.println("Saving Excel Document...");
}
}
Step 3: Creator (Factory)
public abstract class DocumentFactory {
// Factory method
public abstract Document createDocument();
// Common logic (optional)
public void processDocument() {
Document doc = createDocument();
doc.open();
doc.save();
}
}
Step 4: Concrete Factories
public class WordDocumentFactory extends DocumentFactory {
@Override
public Document createDocument() {
return new WordDocument();
}
}
public class PdfDocumentFactory extends DocumentFactory {
@Override
public Document createDocument() {
return new PdfDocument();
}
}
public class ExcelDocumentFactory extends DocumentFactory {
@Override
public Document createDocument() {
return new ExcelDocument();
}
}
Step 5: Client Code
public class DocumentApp {
public static void main(String[] args) {
DocumentFactory factory;
// Suppose client needs a PDF document
factory = new PdfDocumentFactory();
factory.processDocument();
// Client needs a Word document
factory = new WordDocumentFactory();
factory.processDocument();
}
}
Output
Opening PDF Document...
Saving PDF Document...
Opening Word Document...
Saving Word Document...
Notice how the client code (DocumentApp) never directly creates a new WordDocument() or new PdfDocument(). Instead, it relies on the factory method.
Real-World Analogy
Think of a coffee shop.
- You don’t make your coffee yourself. You place an order (the request).
- The barista (factory) decides how to prepare it.
- Whether you get an espresso, cappuccino, or latte depends on which barista (factory subclass) you interact with.
You just say “I want coffee” (the abstraction). The details of how it’s made are hidden.
Advantages of Factory Method
- Loose Coupling: Client code depends only on the abstraction, not concrete classes.
- Open/Closed Principle: Adding a new product (e.g., PowerPointDocument) requires a new factory, not changes to existing code.
- Centralized Object Creation: Simplifies testing and configuration.
Disadvantages
- Increased Number of Classes: Every new product requires a corresponding factory class.
- Slight Complexity Overhead: For small projects, this might feel like overengineering.
When to Use
- When the exact type of object to be created is determined at runtime.
- When you want to avoid tight coupling between client code and concrete classes.
- When adding new product types frequently.
Conclusion
The Factory Method Pattern is a cornerstone of creational design patterns. It provides a clean way to encapsulate object creation, ensuring flexibility, maintainability, and scalability.
In this, we created a custom Document Processing System example to illustrate how factories abstract object creation. In real-world Java, you’ll find this pattern everywhere — from Calendar.getInstance() to NumberFormat.getInstance().
In the next blog, we will explore the Abstract Factory Pattern, which takes this concept one step further by dealing with families of related objects.