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

Product + operation() ConcreteProduct Creator + factoryMethod() ConcreteCreator + factoryMethod() ​​​
Factory Method UML Structure

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() calls factoryMethod().
  • 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

AspectSimple FactoryFactory 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_ptr over raw pointers.
  • Do not use Factory Method for trivial object creation.
  • Keep the design aligned with actual variability requirements.
© 2026 Air Supply Information Center (Air Supply BBS)