This content originally appeared on DEV Community and was authored by Priyank Bhardwaj
Introduction
Design patterns are like battle-tested solutions to common problems in software design. One such pattern is the Abstract Factory Pattern, which falls under Creational Design Patterns. In this article, we’ll break it down with a real-world analogy, implement it in Java, and explain each step thoroughly.
What is the Abstract Factory Pattern?
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Think of it as a super-factory, also known as a factory of factories.
When to Use?
- When your code needs to work with various families of related products.
- When you want to enforce consistency among objects.
- When you want to hide the creation logic from the client code.
Real-Life Analogy
Imagine a Furniture Store
You sell Modern and Victorian styles of furniture. Each style has:
- A Chair
- A Sofa
You don’t want customers (client code) to create ModernChair or VictorianSofa directly. Instead, you give them a FurnitureFactory, which gives them appropriate combinations of products.
Class Diagram Overview
+---------------------+
| FurnitureFactory |<-- Abstract Factory
+---------------------+
| + createChair() |
| + createSofa() |
+---------------------+
/ \
/ \
+------------------------+ +---------------------------+
| ModernFurnitureFactory | | VictorianFurnitureFactory |
+------------------------+ +---------------------------+
| + createChair() | | + createChair() |
| + createSofa() | | + createSofa() |
+------------------------+ +---------------------------+
^ ^
| |
+---------------+ +-----------------+
| ModernChair | | VictorianChair |
+---------------+ +-----------------+
+---------------+ +-----------------+
| ModernSofa | | VictorianSofa |
+---------------+ +-----------------+
Let’s Implement It in Java
Step 1: Abstract Product Interfaces
// Chair.java
public interface Chair {
void sitOn();
}
// Sofa.java
public interface Sofa {
void lieOn();
}
Step 2: Concrete Products
// ModernChair.java
public class ModernChair implements Chair {
public void sitOn() {
System.out.println("Sitting on a modern chair.");
}
}
// VictorianChair.java
public class VictorianChair implements Chair {
public void sitOn() {
System.out.println("Sitting on a Victorian chair.");
}
}
// ModernSofa.java
public class ModernSofa implements Sofa {
public void lieOn() {
System.out.println("Lying on a modern sofa.");
}
}
// VictorianSofa.java
public class VictorianSofa implements Sofa {
public void lieOn() {
System.out.println("Lying on a Victorian sofa.");
}
}
Step 3: Abstract Factory
// FurnitureFactory.java
public interface FurnitureFactory {
Chair createChair();
Sofa createSofa();
}
Step 4: Concrete Factories
// ModernFurnitureFactory.java
public class ModernFurnitureFactory implements FurnitureFactory {
public Chair createChair() {
return new ModernChair();
}
public Sofa createSofa() {
return new ModernSofa();
}
}
// VictorianFurnitureFactory.java
public class VictorianFurnitureFactory implements FurnitureFactory {
public Chair createChair() {
return new VictorianChair();
}
public Sofa createSofa() {
return new VictorianSofa();
}
}
Step 5: Client Code
// Application.java
public class Application {
private Chair chair;
private Sofa sofa;
public Application(FurnitureFactory factory) {
chair = factory.createChair();
sofa = factory.createSofa();
}
public void run() {
chair.sitOn();
sofa.lieOn();
}
public static void main(String[] args) {
FurnitureFactory modernFactory = new ModernFurnitureFactory();
Application modernApp = new Application(modernFactory);
modernApp.run();
System.out.println("------");
FurnitureFactory victorianFactory = new VictorianFurnitureFactory();
Application victorianApp = new Application(victorianFactory);
victorianApp.run();
}
}
Output
Sitting on a modern chair.
Lying on a modern sofa.
------
Sitting on a Victorian chair.
Lying on a Victorian sofa.
Explanation
- Chair & Sofa are the product interfaces.
- ModernChair, VictorianSofa, etc., are concrete products.
- FurnitureFactory is the abstract factory.
- ModernFurnitureFactory & VictorianFurnitureFactory are concrete factories.
- The Application is the client that depends only on the abstract factory, not on specific classes.
This ensures:
- Consistency: Modern factory only returns modern products.
- Flexibility: We can add new families like
FuturisticFurnitureFactory
without changing client code.
Benefits
- Loose coupling: Client code doesn’t need to know about concrete classes.
- Scalability: New product families can be added easily.
- Consistency: Ensures that related products are used together.
Drawbacks
- Can become complex with too many products.
- Adding a new product type (e.g., Table) requires changes in all factories.
Conclusion
The Abstract Factory Pattern is a powerful tool when you need to create families of related objects without binding your code to their concrete classes.
It might seem verbose initially, but the long-term flexibility and maintainability it offers is worth the effort.
This is Part 3 of the Java Design Patterns Series.
If you find it insightful, please share your feedback. Also let me know if you have used factory pattern in your projects.
Next Up: Prototype Pattern – Cloning Without the Mess!
This content originally appeared on DEV Community and was authored by Priyank Bhardwaj