Factory Pattern Implementation Methods
Factory Pattern Implementation Methods
Core Concepts
The Factory Pattern is a structural design pattern that addresses the issue of object creation in a decoupled manner. It abstracts the process of instantiating objects, allowing clients to interact with objects without knowing their specific creation logic. The pattern separates the object creation logic from the client code, promoting better encapsulation and flexibility.
Implementation Methods
1. Simple Factory
The Simple Factory Pattern involves a central factory class that returns instances of objects based on parameters. It is suitable for scenarios where the object creation logic is simple and does not require complex dependencies.
Example:
class Product {
public:
virtual void use() = 0;
virtual ~Product() {}
};
class ConcreteProductA : public Product {
public:
void use() override { std::cout << "Using ConcreteProductA" << std::endl; }
};
class ConcreteProductB : public Product {
public:
void use() override { std::cout << "Using ConcreteProductB" << std::endl; }
};
class ProductFactory {
public:
static Product* createProduct(int type) {
if (type == 1) return new ConcreteProductA();
else if (type == 2) return new ConcreteProductB();
return nullptr;
}
};
Use Case:
When the object creation logic is straightforward and does not require complex dependencies.
2. Abstract Factory
The Abstract Factory Pattern defines an interface for creating families of related objects. It ensures that the client code remains decoupled from the specific implementations.
Example:
class AbstractProductA {
public:
virtual void use() = 0;
virtual ~AbstractProductA() {}
};
class AbstractProductB {
public:
virtual void use() = 0;
virtual ~AbstractProductB() {}
};
class ConcreteProductA : public AbstractProductA {
public:
void use() override { std::cout << "Using ConcreteProductA" << std::endl; }
};
class ConcreteProductB : public AbstractProductB {
public:
void use() override { std::cout << "Using ConcreteProductB" << std::endl; }
};
class AbstractFactory {
public:
virtual AbstractProductA* createA() = 0;
virtual AbstractProductB* createB() = 0;
virtual ~AbstractFactory() {}
};
class ConcreteFactory : public AbstractFactory {
public:
AbstractProductA* createA() override { return new ConcreteProductA(); }
AbstractProductB* createB() override { return new ConcreteProductB(); }
};
Use Case:
When a group of objects must be created together and their creation logic is complex or varies.
3. Factory Method
The Factory Method Pattern defines an interface for creating objects in a framework, allowing subclasses to alter the type of objects that are created. It provides a way to delay object creation until the client requests it.
Example:
class AbstractProduct {
public:
virtual void use() = 0;
virtual ~AbstractProduct() {}
};
class ConcreteProduct : public AbstractProduct {
public:
void use() override { std::cout << "Using ConcreteProduct" << std::endl; }
};
class AbstractFactory {
public:
virtual AbstractProduct* createProduct() = 0;
virtual ~AbstractFactory() {}
};
class ConcreteFactory : public AbstractFactory {
public:
AbstractProduct* createProduct() override {
return new ConcreteProduct();
}
};
Use Case:
When the client needs to control the object creation process, such as in scenarios with dynamic configurations.
4. Singleton
The Singleton Pattern ensures that a class has only one instance and provides a global point of access to that instance. It is useful when a single instance is needed throughout the application.
Example:
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
void doSomething() {
std::cout << "Singleton is doing something" << std::endl;
}
private:
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
int main() {
Singleton::getInstance().doSomething();
return 0;
}
Use Case:
When a class needs to control its own instantiation, such as in logging or configuration management.
Examples
The Factory Pattern is often used in scenarios where the creation of objects is complex or varies based on external factors. For instance, in a game engine, different game modes might require different object instantiations, and the Factory Pattern helps manage this without exposing the client to the specific creation logic.
Conclusion
The Factory Pattern provides a flexible and decoupled way to manage object creation in C++. By implementing methods like Simple Factory, Abstract Factory, Factory Method, and Singleton, developers can encapsulate object creation logic, improve maintainability, and reduce coupling between client code and object creation processes. Each method has its own use cases and trade-offs, and choosing the right approach depends on the specific requirements of the application.