Design Pattern - Factory Pattern
01 Factory Pattern
Background
When writing code, we often encounter such scenarios:
- 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.
- The object’s creation logic is complex. If scattered throughout the code, it will cause maintenance difficulties.
- 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
- Decoupling: Client code doesn’t need to care about the specific object creation process
- Easy to extend: Adding new products only requires modifying the factory class, without affecting the client
- Unified management: All object creation is in one place, making maintenance easier
Variants
- Simple Factory: Factory class directly creates objects based on parameters (the example above is actually a simple factory)
- Factory Method: Defines an interface for creating objects, letting subclasses decide which class to instantiate
- 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.