Learn the Factory Method Design Pattern in Java — what problem it solves, how it works, practical Java examples, benefits, drawbacks, and how modern frameworks like Spring internally use factory-based object creation.
In the previous article, we explored the Singleton Pattern — the most famous and often most misunderstood Creational Design Pattern.
Now let’s move to another extremely practical pattern that developers use almost every day, often without realizing it:
Factory Method Pattern
If Singleton answers:
“How do I ensure only one instance exists?”
Factory Method answers:
“How do I create objects without tightly coupling my code to concrete implementations?”
This pattern becomes incredibly useful as applications grow.
The Problem: Hardcoded Object Creation
Let’s begin with a real-world example.
Suppose you’re building a payment system.
Initial implementation:
public class PaymentService {
public void process(String mode) {
if(mode.equals("CARD")) {
CardPayment payment =
new CardPayment();
payment.pay();
}
else if(mode.equals("UPI")) {
UpiPayment payment =
new UpiPayment();
payment.pay();
}
}
}
Looks fine.
Until business requirements evolve.
Now product teams want:
- Wallet Payments
- PayPal
- Stripe
- Apple Pay
- Google Pay
Your code starts growing.
if
else if
else if
else if
else if
Problems emerge.
Problems With Hardcoded new
1. Tight Coupling
Your service knows every implementation.
new CardPayment()
new UpiPayment()
new WalletPayment()
Service layer becomes coupled to concrete classes.
2. Difficult Extension
Adding a new payment method requires modifying existing logic.
Violates Open/Closed Principle.
3. Testing Becomes Harder
Mocking concrete dependencies becomes painful.
4. Scattered Creation Logic
Object creation spreads across the codebase.
Maintenance complexity increases.
This is exactly where Factory Method Pattern helps.
What is Factory Method Pattern?
Factory Method is a Creational Design Pattern that:
Delegates object creation to a dedicated method or factory component instead of directly using
new.
Instead of writing:
new Object()
everywhere,
we centralize creation logic.
Basic Factory Method Implementation
Step 1 — Define Common Contract
public interface Payment {
void pay();
}
Step 2 — Create Implementations
Card Payment
public class CardPayment
implements Payment {
public void pay() {
System.out.println(
"Card Payment Processed");
}
}
UPI Payment
public class UpiPayment
implements Payment {
public void pay() {
System.out.println(
"UPI Payment Processed");
}
}
Step 3 — Create Factory
public class PaymentFactory {
public static Payment
getPayment(String mode) {
if(mode.equals("CARD"))
return new CardPayment();
if(mode.equals("UPI"))
return new UpiPayment();
throw new IllegalArgumentException(
"Unsupported Payment");
}
}
Step 4 — Use Factory
Service layer becomes cleaner.
public class PaymentService {
public void process(String mode) {
Payment payment =
PaymentFactory
.getPayment(mode);
payment.pay();
}
}
Notice what changed.
Service no longer knows:
CardPayment
UpiPayment
WalletPayment
It only understands:
Payment Interface
This reduces coupling significantly.
How Factory Method Works Internally
Think of Factory as a specialized object creation manager.
Instead of:
Client
│
▼
new ConcreteClass()
we get:
Client
│
▼
Factory
│
▼
Concrete Object
Creation responsibility moves into one centralized place.
Real Engineering Example — Notification Service
Suppose your application sends notifications.
Without Factory:
public void notify(String type) {
if(type.equals("EMAIL")) {
EmailNotification email =
new EmailNotification();
email.send();
}
else if(type.equals("SMS")) {
SmsNotification sms =
new SmsNotification();
sms.send();
}
}
Again:
Growing conditionals.
Growing coupling.
Factory Version
Interface:
public interface Notification {
void send();
}
Implementations:
public class EmailNotification
implements Notification {
public void send() {
System.out.println(
"Email Sent");
}
}
public class SmsNotification
implements Notification {
public void send() {
System.out.println(
"SMS Sent");
}
}
Factory:
public class NotificationFactory {
public static Notification
create(String type) {
if(type.equals("EMAIL"))
return new EmailNotification();
if(type.equals("SMS"))
return new SmsNotification();
throw new IllegalArgumentException();
}
}
Usage:
Notification notification =
NotificationFactory
.create("EMAIL");
notification.send();
Cleaner.
More maintainable.
More extensible.
Benefits of Factory Method Pattern
1. Reduced Coupling
Consumers depend on abstractions.
Not concrete implementations.
2. Centralized Creation Logic
All construction logic lives in one location.
Easier maintenance.
3. Improved Extensibility
Adding new implementations becomes easier.
4. Better Testability
Factories simplify dependency substitution.
Mocking becomes cleaner.
5. Supports Open/Closed Principle
You extend behavior.
You avoid modifying stable business logic.
Factory Method vs Direct Object Creation
Without Factory:
CardPayment payment =
new CardPayment();
Problems:
✔ Hard dependency
✔ Concrete coupling
✔ Reduced flexibility
With Factory:
Payment payment =
PaymentFactory
.getPayment("CARD");
Benefits:
✔ Interface driven
✔ Cleaner abstraction
✔ Easier replacement
Where Factory Pattern Appears in Real Frameworks
You already use Factory concepts daily.
Java Calendar API
Example:
Calendar calendar =
Calendar.getInstance();
You don’t create:
new GregorianCalendar()
directly.
Factory decides implementation.
Spring Framework
Spring container behaves heavily like a factory.
Example:
ApplicationContext
creates:
- services
- repositories
- controllers
- configuration beans
You usually do not write:
new UserService()
manually.
Spring manages creation.
JDBC DriverManager
Example:
Connection connection =
DriverManager.getConnection();
Factory-based creation.
LoggerFactory
SLF4J example:
Logger log =
LoggerFactory
.getLogger(MyClass.class);
Another factory abstraction.
Factory Method vs Simple Factory
Many developers confuse these concepts.
Simple Factory
Single utility class.
PaymentFactory.getPayment()
Factory Method Pattern (GoF Version)
Uses inheritance.
Subclass controls creation.
Example structure:
Creator
│
├── ConcreteCreatorA
└── ConcreteCreatorB
This provides higher flexibility.
Most day-to-day Java applications often start with Simple Factory style.
Common Mistakes With Factory Pattern
Mistake 1 — Factory Explosion
Creating factories for trivial objects.
Overengineering risk.
Mistake 2 — Massive if-else Factories
Large factories become new bottlenecks.
Example:
1000 lines
50 conditions
Factory itself becomes messy.
Mistake 3 — Using Factory Without Need
Sometimes:
new User()
is perfectly acceptable.
Patterns should solve problems.
Not create complexity.
When Should You Use Factory Method?
Good candidates:
✔ Multiple implementations exist
✔ Object creation logic varies
✔ Runtime implementation selection needed
✔ Loose coupling required
Avoid when:
❌ Object creation is trivial
❌ No abstraction benefit exists
❌ Complexity outweighs value
Quick Comparison: Singleton vs Factory Method
| Pattern | Focus |
|---|---|
| Singleton | Ensure one instance |
| Factory Method | Delegate object creation |
Singleton controls:
HOW MANY objects exist.
Factory controls:
HOW objects are created.
Final Thoughts
The Factory Method Pattern solves a very common engineering challenge:
How do we create objects cleanly without scattering
neweverywhere?
By centralizing creation logic, Factory patterns improve:
- maintainability
- flexibility
- extensibility
- testability
This is one reason factories appear heavily across:
- Spring Framework
- Java SDK
- Logging libraries
- JDBC
- Enterprise applications
Once you begin recognizing factory-based design, you’ll notice it everywhere in Java ecosystems.
In the next article, we’ll continue our Creational Design Pattern deep dive with another highly practical pattern: