Factory Design Pattern in Java — A Practical Guide
Understand the Factory (creational) pattern with real-world analogies, Java code, diagrams, and interview tips — ready for publishing on DevForgeHub.
Introduction
The Factory Design Pattern is a creational pattern that centralizes object creation logic into a factory class. Instead of instantiating objects directly, client code requests objects from the factory. This separates what is created from how it is created, enabling greater flexibility and easier maintenance.
Real-world analogy
Think of Tata Motors producing multiple car variants: petrol, diesel, and electric. A single factory line (or a factory class) receives a request for a car type and returns the right model. The client (customer) doesn't need to know the assembly details — it just asks for the type and uses the product.
What is the Factory Pattern?
Definition: The Factory Pattern encapsulates object creation in a separate component (factory), usually returning objects through a common interface or abstract class. It's classified as a creational pattern because it deals with object instantiation.
When and why to use it
- When instantiation logic is complex or depends on runtime parameters.
- When you want to decouple client code from concrete classes.
- When the system must choose between several related classes.
- To centralize creation for easier testing and extension (OCP).
Example scenario: OS object creation
Imagine a module that needs an OperatingSystem implementation depending on a type string (e.g., "linux", "windows"). Instead of `if/else` or `switch` spread across clients, we use OperatingSystemFactory to return the right object.
Step-by-step implementation (Java)
1. Define the interface
public interface OperatingSystem {
void boot();
String getName();
}
2. Concrete implementations
public class LinuxOperatingSystem implements OperatingSystem {
@Override
public void boot() {
System.out.println("Booting Linux...");
}
@Override
public String getName() { return "Linux"; }
}
public class WindowsOperatingSystem implements OperatingSystem {
@Override
public void boot() {
System.out.println("Booting Windows...");
}
@Override
public String getName() { return "Windows"; }
}
public class MacOperatingSystem implements OperatingSystem {
@Override
public void boot() {
System.out.println("Booting macOS...");
}
@Override
public String getName() { return "macOS"; }
}
3. Factory class
public class OperatingSystemFactory {
public static OperatingSystem create(String type) {
if (type == null) return null;
switch (type.toLowerCase()) {
case "linux": return new LinuxOperatingSystem();
case "windows": return new WindowsOperatingSystem();
case "mac": return new MacOperatingSystem();
default: throw new IllegalArgumentException("Unknown OS type: " + type);
}
}
}
4. Client usage (Main)
public class FactoryDemo {
public static void main(String[] args) {
OperatingSystem os1 = OperatingSystemFactory.create("linux");
os1.boot(); // Booting Linux...
OperatingSystem os2 = OperatingSystemFactory.create("windows");
os2.boot(); // Booting Windows...
}
}
Diagram description: Client code calls OperatingSystemFactory.create(type). The factory returns an instance that implements OperatingSystem (LinuxOperatingSystem, WindowsOperatingSystem, MacOperatingSystem). The client depends on the OperatingSystem interface, not the concrete classes.
Key benefits
- Decoupling: Clients depend on abstractions, not concrete implementations.
- Single responsibility: Creation logic is centralized.
- Open/Closed: Add new products by extending the factory without changing client code.
- Testability: Easier mocking of product creation in unit tests.
Variations & patterns related to Factory
There are several related patterns: Simple Factory (a static factory method, as shown), Factory Method (subclass decides which class to instantiate), and Abstract Factory (creates families of related objects). Choose based on the complexity and variability of products.
When not to use Factory
- When there are only one or two concrete classes and creating them directly is simpler and clearer.
- When factory adds unnecessary indirection without real benefit.
Interview Tips
- Explain motivation: Start with the problem of coupling client to concrete classes.
- Give analogy: Use Tata Motors example or a pizza shop making different pizzas.
- Show code: Walk through interface → concrete classes → factory → client.
- Mention trade-offs: Indirection vs flexibility; when it’s overkill.
- Related patterns: Be ready to contrast Factory Method and Abstract Factory.
- Real-world uses: Plugin loaders, object creation based on config, choosing implementations by runtime properties.
Textual UML (for accessibility)
UML (textual):
Client -> OperatingSystemFactory : create(type)
OperatingSystemFactory -> LinuxOperatingSystem | WindowsOperatingSystem | MacOperatingSystem : new()
Client -> OperatingSystem : uses methods (boot(), getName())
Advanced: Extending the factory without modifying it
For large systems, replace a switch/case with a registration-based factory or use dependency injection. Example: a map of creators keyed by type string.
public class RegistryOperatingSystemFactory {
private static final Map();
static {
registry.put("linux", LinuxOperatingSystem::new);
registry.put("windows", WindowsOperatingSystem::new);
registry.put("mac", MacOperatingSystem::new);
}
public static void register(String key, Supplier<OperatingSystem> creator) {
registry.put(key.toLowerCase(), creator);
}
public static OperatingSystem create(String key) {
Supplier<OperatingSystem> creator = registry.get(key.toLowerCase());
if (creator == null) throw new IllegalArgumentException("Unknown key: " + key);
return creator.get();
}
}
Summary
The Factory Pattern simplifies object creation, encapsulates instantiation logic, and decouples clients from concrete implementations — making systems easier to maintain and extend. Use it when creation decisions are complex or likely to change.
Call to action
If this helped, drop a comment with your questions, subscribe for more design-pattern tutorials, and share the post with your peers. Want me to convert this into a downloadable cheat-sheet or include live diagrams? Tell me which one.
Comments
Post a Comment