Factory Method Pattern
TL;DR
- Category: Creational Pattern
- Purpose: Delegate object creation to subclasses.
- Core Idea: Create objects through a common interface without specifying concrete classes.
- When to Use: When the object type varies and should be determined at runtime.
- Main Benefit: Reduces tight coupling between client and concrete classes.
1. What is the Factory Method Pattern?
The Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate.
In short, Object creation is delegated to subclasses rather than hard-coded in client code.
2. Why Do We Need Factory Method?
❌ The Problem Without Factory Method
class Application {
public:
void run() {
Button* btn = new WindowsButton(); // Hard-coded dependency
btn->render();
}
};
Problems:
- Tight coupling to
WindowsButton - Hard to extend for Mac/Linux
- Violates Open-Closed Principle (OCP)
- Requires modifying existing code to add new types
✅ What Factory Method Solves
- Removes direct dependency on concrete classes
- Encapsulates object creation logic
- Supports runtime flexibility
- Improves extensibility
3. Structure of Factory Method
Key Components
- Product – Abstract base class/interface (Defines the base type of created objects)
- ConcreteProduct – Actual implementation (Implement the product interface)
- Creator – Declares factory method
- ConcreteCreator (ConcreteFactories) – Implements factory method (Decides which concrete product to instantiate)
4. UML Diagram
5. C++ Implementation Example
Product Interface
#include <iostream>
#include <memory>
class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};
Concrete Products
class WindowsButton : public Button {
public:
void render() override {
std::cout << "Render Windows Button\n";
}
};
class MacButton : public Button {
public:
void render() override {
std::cout << "Render Mac Button\n";
}
};
Creator (Declares Factory Method)
class Dialog {
public:
void createAndRender() {
std::unique_ptr<Button> btn(factoryMethod());
btn->render();
}
virtual Button* factoryMethod() = 0;
virtual ~Dialog() = default;
};
Concrete Creators
class WindowsDialog : public Dialog {
public:
Button* factoryMethod() override {
return new WindowsButton();
}
};
class MacDialog : public Dialog {
public:
Button* factoryMethod() override {
return new MacButton();
}
};
Usage Example (Client Code)
The client code does not depend on concrete product classes. It works with the abstract base class Dialog.
int main() {
std::unique_ptr<Dialog> dialog;
// Runtime decision (could be based on OS, config file, etc.)
std::string platform = "Windows";
if (platform == "Windows") {
dialog = std::make_unique<WindowsDialog>();
} else {
dialog = std::make_unique<MacDialog>();
}
// Client code calls common interface
dialog->createAndRender();
return 0;
}
What Happens at Runtime?
- The client chooses a ConcreteCreator (e.g.,
WindowsDialog). createAndRender()callsfactoryMethod().- The overridden factory method creates the correct ConcreteProduct.
- The product is used via the abstract interface
Button.
Notice that main() never directly creates WindowsButton or MacButton. Object creation responsibility is delegated to subclasses.
Modern Improvement (Returning smart pointer directly)
To avoid raw pointers entirely, we can improve the factory method:
class Dialog {
public:
void createAndRender() {
auto btn = factoryMethod();
btn->render();
}
virtual std::unique_ptr<Button> factoryMethod() = 0;
};
class WindowsDialog : public Dialog {
public:
std::unique_ptr<Button> factoryMethod() override {
return std::make_unique<WindowsButton>();
}
};
This version is safer and fully RAII-compliant.
6. Advantages
- Reduces tight coupling
- Follows the Open-Closed Principle
- Encapsulates object creation logic
- Improves scalability
7. Disadvantages
- Introduces additional classes
- Can increase design complexity
- Maybe over-engineering for small systems
8. When to Use Factory Method
- Object type determined at runtime
- Framework design where subclass customization is required
- Plugin-based architectures
- Cross-platform UI components
9. Correct Use vs Incorrect Use
✅ Correct Uses (Good Examples)
A. Want to isolate object creation behind an interface
Diaglog* diaglog = new WindowsDiaglog();
diaglog->createAndRender(); // Factory Method used, no direct "new WindowsButton"
B. Need to add new product types without modifying client code
Add LinuxButton + AndroidButton. Client remains unchanged.
❌ Incorrect Uses (Bad Examples)
A. Using Factory Method when no polymorphism is needed
For simple objects with no inheritance, this pattern adds unnecessary complexity.
B. Factory Method returning multiple unrelated products
Buttont* createAndRender() {
if (...) return new WindowsButton();
else return new Car(); // wrong! different product hierarchy
}
C. Using Factory Method when a simple constructor or lambda would do
Sometimes a lambda or function pointer is enough:
auto factory = [] { return MyObject(42); };
10. Factory Method vs Simple Factory
| Aspect | Simple Factory | Factory Method |
|---|---|---|
| Inheritance Required | No | Yes |
| Open-Closed Principle | Often Violated | Respected |
| Extensibility | Limited | High |
11. Common Interview Questions
Q1: What problem does Factory Method solve?
It removes tight coupling between client and concrete classes.
Q2: How is Factory Method different from Abstract Factory?
Factory Method creates one product at a time. Abstract Factory creates families of related products.
Q3: Does Factory Method improve performance?
No. It improves architecture, not runtime performance.
Q4: What principle does it support?
Open-Closed Principle and Dependency Inversion Principle.
12. Final Engineering Recommendations
- Use Factory Method when object creation varies by subclass.
- Prefer
std::unique_ptrover raw pointers. - Do not use Factory Method for trivial object creation.
- Keep the design aligned with actual variability requirements.