Simple Factory Pattern
The simple factory pattern is one of the most widely used creation patterns in object-oriented software. It is not listed among the twenty-three classic patterns from the GoF book, yet it is often treated as a practical entry point into factory design. Its popularity comes from a simple promise: keep object creation in one place and let clients depend on abstractions rather than concrete classes.
The pattern does not require a large inheritance hierarchy. Instead, it wraps object creation in a factory class. The client asks the factory for an object. The factory returns an instance that matches the request.
The Core Idea
At its center is the separation of object creation and object usage. A client may need a payment processor, a logger, a controller, or a serializer. The simple factory pattern puts the logic that decides which concrete class to instantiate into a factory.
The client then depends on an interface or an abstract type. This reduces the need to know how an object is constructed. It also makes it easier to change implementations without rewriting client code.
This becomes useful when constructors are complex, when configuration affects the final type, or when a team wants a single entry point for creating objects.
Main Components
A simple factory usually has three parts:
- Factory: creates and returns product instances.
- Abstract product: defines the common contract.
- Concrete products: implement the contract.
The factory may use parameters, keys, configuration values, or conditional branches to choose a product. The exact mechanism can vary. The important point is that creation logic is centralized.
Example: Payment Factory
Imagine a system that accepts different payment methods. The client should not create AliPay or WechatPay directly. It should ask a factory for a payment object.
First, define the common interface:
public interface Payment {
void pay(double amount);
}
Next, create concrete implementations:
public class AliPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("Pay with Ali: " + amount);
}
}
public class WechatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("Pay with WeChat: " + amount);
}
}
Then create the factory:
public class PaymentFactory {
public static Payment create(String type) {
if ("ali".equals(type)) {
return new AliPay();
}
if ("wechat".equals(type)) {
return new WechatPay();
}
throw new IllegalArgumentException("Unsupported payment type: " + type);
}
}
Finally, let the client use the abstraction:
Payment payment = PaymentFactory.create("ali");
payment.pay(99.9);
The client depends on the Payment interface and the factory. It does not need to know the concrete implementations. When the product list changes, the factory can be adjusted while the client remains mostly stable.
Benefits
The simple factory pattern brings several advantages:
- It centralizes creation logic.
- It reduces coupling between clients and concrete classes.
- It gives teams a clear place to manage object creation.
- It can make early code easier to test and extend.
By using an interface, clients can work with objects through a stable contract. The factory handles the details of choosing and building the right object.
This separation can also make future changes easier to reason about. If a new product must be added, the team may still need to touch the factory. However, the client code does not have to change at the same time.
Drawbacks
The pattern is not without problems. It may break the open/closed principle when new products are added. A developer often needs to modify the factory rather than only add new code. Over time, the factory can become large and hard to maintain.
The conditional logic may also become messy if many product types exist. Static factory methods may be difficult to override. The pattern may not offer enough flexibility for complex creation processes.
In addition, the simple factory can hide important decisions inside one class. That may be fine for small systems, but larger applications might benefit from more structured approaches.
When to Use It
The simple factory pattern is suitable when:
- There are only a few product types.
- The product family is unlikely to expand quickly.
- Creation logic is simple.
- Clients do not need custom construction.
It is common in small systems, prototypes, and modules with stable requirements. Frameworks sometimes use similar ideas when returning objects based on configuration.
Final Thoughts
The simple factory pattern offers a lightweight way to separate object creation from object use. It is easy to implement and easy to understand. For small projects, it can be a sensible choice.
As systems grow, however, developers may need more advanced factory patterns. Understanding the simple factory is often the first step toward mastering the wider factory family.