01 Factory Pattern

Background

When writing code, we often encounter such scenarios:

  1. Need to create different objects based on different types of user input. For example, based on the database type in the configuration file, create connection objects for MySQL, PostgreSQL, or MongoDB.
  2. The object’s creation logic is complex. If scattered throughout the code, it will cause maintenance difficulties.
  3. Want to unify the management of object creation to facilitate adding new product types.

Therefore, Factory Pattern came into being.

Analysis

The core idea of the factory pattern is: separate object creation from usage.

Here is its UML diagram:

classDiagram
    class Product {
        <>
        +operation()
    }
    class ConcreteProductA {
        +operation()
    }
    class ConcreteProductB {
        +operation()
    }
    class Creator {
        +factoryMethod() Product
    }
    class ConcreteCreatorA {
        +factoryMethod() Product
    }
    class ConcreteCreatorB {
        +factoryMethod() Product
    }
    
    Product <|.. ConcreteProductA
    Product <|.. ConcreteProductB
    Creator <|.. ConcreteCreatorA
    Creator <|.. ConcreteCreatorB
    Creator ..> Product

Example

In C++, factory patterns are very commonly used. Here is a simple example:

1. Define Product Interface

class Database {
public:
    virtual ~Database() = default;
    virtual void connect() = 0;
    virtual void query(const std::string& sql) = 0;
};

2. Implement Concrete Products

class MySQL : public Database {
public:
    void connect() override {
        std::cout << "Connecting to MySQL..." << std::endl;
    }
    void query(const std::string& sql) override {
        std::cout << "MySQL executing: " << sql << std::endl;
    }
};

class PostgreSQL : public Database {
public:
    void connect() override {
        std::cout << "Connecting to PostgreSQL..." << std::endl;
    }
    void query(const std::string& sql) override {
        std::cout << "PostgreSQL executing: " << sql << std::endl;
    }
};

3. Define Factory

class DatabaseFactory {
public:
    enum class Type {
        MySQL,
        PostgreSQL
    };
    
    static std::unique_ptr<Database> create(Type type) {
        switch (type) {
            case Type::MySQL:
                return std::make_unique<MySQL>();
            case Type::PostgreSQL:
                return std::make_unique<PostgreSQL>();
            default:
                return nullptr;
        }
    }
};

4. Usage

int main() {
    auto db = DatabaseFactory::create(DatabaseFactory::Type::MySQL);
    db->connect();
    db->query("SELECT * FROM users");
    
    return 0;
}

Advantages

  1. Decoupling: Client code doesn’t need to care about the specific object creation process
  2. Easy to extend: Adding new products only requires modifying the factory class, without affecting the client
  3. Unified management: All object creation is in one place, making maintenance easier

Variants

  1. Simple Factory: Factory class directly creates objects based on parameters (the example above is actually a simple factory)
  2. Factory Method: Defines an interface for creating objects, letting subclasses decide which class to instantiate
  3. Abstract Factory: Provides an interface for creating a series of related objects without specifying concrete classes

Summary

Factory pattern is one of the most commonly used creational patterns, especially suitable in C++ for:

  • Need to create different objects based on configuration
  • Complex object creation logic
  • Need unified management of object creation

Mastering the factory pattern can make your code more flexible and maintainable.