Limitations of Simple Factory Pattern
The Simple Factory pattern successfully separates object creation from business logic, but it violates the Open-Closed Principle. This principle states that software entities should be open for extension but closed for modification.
Consider a scenario where you need to add a new monster type called Beast. With the Simple Factory approach, you would need to:
- Create a new Beast class inheriting from Monster
- Modify the existing factory's createMonster method
This modification of existing code violates the Open-Closed Principal. The Factory Method pattern provides a better solution.
Factory Method Pattern Solution
The Factory Method pattern, also known as the Polymorphic Factory pattern, offers greater flexibility than Simple Factory at the cost of increased complexity and additional classes.
Key characteristics:
- Each concrete product has its own factory class
- All factory classes inherit from a common abstract factory base
- Factory method is defined in the base factory and implemented in derived factories
- Conforms to the Open-Closed Principle
The pattern defines an interface for creating objects but lets subclasses decide which class to instantiate. This defers instantiation of specific classes to factory subclasses.
Implementation Example
#include <iostream>
using namespace std;
namespace FactoryMethod {
// Base monster class
class Monster {
public:
Monster(int health, int mana, int damage)
: health_(health), mana_(mana), damage_(damage) {}
virtual ~Monster() {}
protected:
int health_;
int mana_;
int damage_;
};
// Concrete monster types
class UndeadMonster : public Monster {
public:
UndeadMonster(int health, int mana, int damage)
: Monster(health, mana, damage) {
cout << "Created Undead Monster - Health: " << health_
<< ", Mana: " << mana_ << ", Damage: " << damage_ << endl;
}
};
class ElementalMonster : public Monster {
public:
ElementalMonster(int health, int mana, int damage)
: Monster(health, mana, damage) {
cout << "Created Elemental Monster - Health: " << health_
<< ", Mana: " << mana_ << ", Damage: " << damage_ << endl;
}
};
class MechanicalMonster : public Monster {
public:
MechanicalMonster(int health, int mana, int damage)
: Monster(health, mana, damage) {
cout << "Created Mechanical Monster - Health: " << health_
<< ", Mana: " << mana_ << ", Damage: " << damage_ << endl;
}
};
// Abstract factory base
class MonsterFactory {
public:
virtual Monster* CreateMonster() = 0;
virtual ~MonsterFactory() {}
};
// Concrete factory classes
class UndeadFactory : public MonsterFactory {
public:
Monster* CreateMonster() override {
return new UndeadMonster(100, 50, 25);
}
};
class ElementalFactory : public MonsterFactory {
public:
Monster* CreateMonster() override {
return new ElementalMonster(80, 100, 30);
}
};
class MechanicalFactory : public MonsterFactory {
public:
Monster* CreateMonster() override {
return new MechanicalMonster(150, 20, 35);
}
};
// Client function using factory method
Monster* CreateMonsterUsingFactory(MonsterFactory* factory) {
return factory->CreateMonster();
}
}
void TestFactoryMethod() {
using namespace FactoryMethod;
MonsterFactory* undeadFactory = new UndeadFactory();
Monster* undead = CreateMonsterUsingFactory(undeadFactory);
MonsterFactory* elementalFactory = new ElementalFactory();
Monster* elemental = CreateMonsterUsingFactory(elementalFactory);
MonsterFactory* mechanicalFactory = new MechanicalFactory();
Monster* mechanical = CreateMonsterUsingFactory(mechanicalFactory);
delete undeadFactory;
delete elementalFactory;
delete mechanicalFactory;
delete undead;
delete elemental;
delete mechanical;
}
int main() {
TestFactoryMethod();
return 0;
}
Pattern Comparison
The Simple Factory pattern centralizes object creation but lacks flexibility. The Factory Method pattern establishes a framework where subclasses determine object creation specifics.
Factory Method requires creating a factory hierarchy that mirrors the product hierarchy, increasing class count and structural complexity.
Template Implementation
// Template-based factory implementation
template <typename T>
class TemplateMonsterFactory : public MonsterFactory {
public:
Monster* CreateMonster(int health, int mana, int damage) override {
return new T(health, mana, damage);
}
};
// Global factory function
Monster* GlobalCreateMonster(MonsterFactory* factory, int health, int mana, int damage) {
return factory->CreateMonster(health, mana, damage);
}
This template approach reduces the number of concrete factory classes while maintaining the pattern's benefits.