Introduction
When designing scalable and maintainable systems, object creation plays a crucial role. Two commonly used creational design patterns—Factory vs Builder—help manage this responsibility in different ways.
Factory Design Pattern
The Factory Pattern provides a way to create objects without exposing the instantiation logic to the client. Instead of calling constructors directly, the client delegates the object creation responsibility to a factory class or method. This helps in decoupling object creation from business logic and promotes flexibility and scalability.
Factory Pattern Example
public class GameCreator {
public Game createGame(Integer maxTimePerMove, Integer maxTimePerPlayer){
return new Game(new GameConfig(
maxTimePerPlayer,
maxTimePerPlayer!=null),
new TicTacToeBoard(),
null,
0,
maxTimePerPlayer,
maxTimePerMove
);
}
public Game createGame(Integer maxTimePerMove, Integer maxTimePerPlayer, TicTacToeBoard board){
return new Game(new GameConfig(
maxTimePerPlayer,
maxTimePerPlayer!=null),
board,
null,
0,
maxTimePerPlayer,
maxTimePerMove
);
}
}
Builder Design Pattern
The Builder Pattern is used to construct complex objects step by step. It allows you to create different representations of an object using the same construction process. This pattern is especially useful when an object has many optional parameters or configurations, helping avoid telescoping constructors and improving code readability.
Builder Pattern Example
public class Game {
private GameConfig config;
private TicTacToeBoard board;
private Integer maxTimePerPlayer;
private Integer maxTimePerMove;
private Game(Builder builder) {
this.config = builder.config;
this.board = builder.board;
this.maxTimePerPlayer = builder.maxTimePerPlayer;
this.maxTimePerMove = builder.maxTimePerMove;
}
public static class Builder {
private GameConfig config;
private TicTacToeBoard board = new TicTacToeBoard(); // default
private Integer maxTimePerPlayer;
private Integer maxTimePerMove;
public Builder maxTimePerPlayer(Integer maxTimePerPlayer) {
this.maxTimePerPlayer = maxTimePerPlayer;
this.config = new GameConfig(
maxTimePerPlayer,
maxTimePerPlayer != null
);
return this;
}
public Builder maxTimePerMove(Integer maxTimePerMove) {
this.maxTimePerMove = maxTimePerMove;
return this;
}
public Builder board(TicTacToeBoard board) {
this.board = board;
return this;
}
public Game build() {
return new Game(this);
}
}
}
Usage Comparison (Factory vs Builder)
Factory Pattern creates an object in a single step whereas, Builder Pattern creates an object step by step giving greater control over configuration.
// Factory Pattern
GameCreator gameCreator = new GameCreator();
Game game = gameCreator.createGame(10, 60);
// Builder Pattern
Game game = new Game.Builder()
.maxTimePerPlayer(60)
.maxTimePerMove(10)
.build();
Factory vs Builder (Key Differences)
| Factory Pattern | Builder Pattern |
| focuses on which object to create | focuses on how to create a complex object |
| Best for simple or moderately complex objects | Designed for highly complex objects with many optional parameters |
| Object is created in a single step | Object is created through multiple steps |
| Usually returns one of several related types (polymorphism) | Can create different representations of the same object using the same construction process |
| When you don’t know the exact type of object to create at runtime | When object construction involves many parameters or configurations (avoiding telescoping constructors) |
Conclusion
Use Factory when you need to decide which object to create.
Use Builder when you need control over how a complex object is created.