1. 工厂方法模式的核心思想
工厂方法模式是面向对象设计中一个经典的设计模式,它属于创建型模式的一种。这个模式的核心思想是将对象的创建过程抽象化,让子类决定实例化哪个类。简单来说,就是定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。
在C++中实现工厂方法模式时,我们通常会遇到几个关键概念:
- 抽象产品(Product):定义了产品的接口
- 具体产品(ConcreteProduct):实现了抽象产品接口的具体类
- 创建者(Creator):声明工厂方法,返回一个产品对象
- 具体创建者(ConcreteCreator):重写工厂方法以返回具体产品实例
提示:工厂方法模式与简单工厂模式不同,它通过让子类决定创建什么对象来实现"对象创建"的延迟。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. C++中工厂方法模式的典型实现
2.1 基础实现结构
让我们先看一个最基本的C++实现框架:
cpp复制// 抽象产品类
class Product {
public:
virtual ~Product() {}
virtual void Operation() = 0;
};
// 具体产品类A
class ConcreteProductA : public Product {
public:
void Operation() override {
std::cout << "ConcreteProductA operation" << std::endl;
}
};
// 具体产品类B
class ConcreteProductB : public Product {
public:
void Operation() override {
std::cout << "ConcreteProductB operation" << std::endl;
}
};
// 抽象创建者类
class Creator {
public:
virtual ~Creator() {}
virtual Product* FactoryMethod() = 0;
void SomeOperation() {
Product* product = FactoryMethod();
product->Operation();
delete product;
}
};
// 具体创建者类A
class ConcreteCreatorA : public Creator {
public:
Product* FactoryMethod() override {
return new ConcreteProductA();
}
};
// 具体创建者类B
public Creator {
public:
Product* FactoryMethod() override {
return new ConcreteProductB();
}
};
2.2 实际应用示例
假设我们正在开发一个图形绘制应用,需要创建不同类型的图形对象。使用工厂方法模式可以这样实现:
cpp复制// 图形接口
class Shape {
public:
virtual ~Shape() {}
virtual void Draw() = 0;
};
// 具体图形类
class Circle : public Shape {
public:
void Draw() override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Rectangle : public Shape {
public:
void Draw() override {
std::cout << "Drawing a rectangle" << std::endl;
}
};
// 图形创建者接口
class ShapeCreator {
public:
virtual ~ShapeCreator() {}
virtual Shape* CreateShape() = 0;
};
// 具体创建者
class CircleCreator : public ShapeCreator {
public:
Shape* CreateShape() override {
return new Circle();
}
};
class RectangleCreator : public ShapeCreator {
public:
Shape* CreateShape() override {
return new Rectangle();
}
};
3. 工厂方法模式的高级应用技巧
3.1 使用模板减少重复代码
在C++中,我们可以利用模板来减少工厂方法模式中的重复代码:
cpp复制template <typename T>
class GenericCreator : public Creator {
public:
Product* FactoryMethod() override {
return new T();
}
};
// 使用方式
GenericCreator<ConcreteProductA> creatorA;
GenericCreator<ConcreteProductB> creatorB;
3.2 结合智能指针管理资源
现代C++推荐使用智能指针来管理资源,我们可以修改工厂方法返回智能指针:
cpp复制#include <memory>
class Creator {
public:
virtual ~Creator() {}
virtual std::unique_ptr<Product> FactoryMethod() = 0;
};
class ConcreteCreatorA : public Creator {
public:
std::unique_ptr<Product> FactoryMethod() override {
return std::make_unique<ConcreteProductA>();
}
};
3.3 工厂方法模式与多态结合
工厂方法模式可以与C++的多态特性完美结合,实现更灵活的对象创建:
cpp复制class AdvancedCreator : public Creator {
public:
virtual std::unique_ptr<Product> FactoryMethod(int type) {
switch(type) {
case 1: return std::make_unique<ConcreteProductA>();
case 2: return std::make_unique<ConcreteProductB>();
default: throw std::invalid_argument("Invalid product type");
}
}
};
4. 工厂方法模式的优缺点与适用场景
4.1 主要优点
- 松耦合:客户端代码只需要知道抽象产品接口,不需要关心具体实现类
- 可扩展性:添加新产品只需添加新的具体产品类和对应的创建者类,不需要修改现有代码
- 单一职责原则:将产品创建逻辑集中在一个地方,便于维护
- 开闭原则:对扩展开放,对修改关闭
4.2 潜在缺点
- 类数量增加:每增加一个产品就需要增加两个类(具体产品和具体创建者)
- 复杂性增加:对于简单对象创建场景可能显得过于复杂
- 需要客户端了解创建者:客户端需要知道使用哪个具体创建者
4.3 最佳适用场景
- 当一个类无法预知它必须创建的对象的类时
- 当一个类希望由其子类来指定它所创建的对象时
- 当需要将类的实例化延迟到子类时
- 在框架设计中,需要为特定实现提供扩展点
注意:在简单对象创建场景中,直接使用new可能更合适,不要为了使用模式而使用模式。
5. 工厂方法模式在实际项目中的应用案例
5.1 跨平台UI组件创建
在开发跨平台UI框架时,工厂方法模式非常有用。例如,创建按钮控件:
cpp复制// 抽象按钮接口
class Button {
public:
virtual ~Button() {}
virtual void Render() = 0;
virtual void OnClick() = 0;
};
// 平台具体实现
class WindowsButton : public Button {
void Render() override { /* Windows特有渲染逻辑 */ }
void OnClick() override { /* Windows特有点击处理 */ }
};
class MacOSButton : public Button {
void Render() override { /* MacOS特有渲染逻辑 */ }
void OnClick() override { /* MacOS特有点击处理 */ }
};
// 按钮创建者
class ButtonCreator {
public:
virtual std::unique_ptr<Button> CreateButton() = 0;
};
// 平台特定创建者
class WindowsButtonCreator : public ButtonCreator {
public:
std::unique_ptr<Button> CreateButton() override {
return std::make_unique<WindowsButton>();
}
};
class MacOSButtonCreator : public ButtonCreator {
public:
std::unique_ptr<Button> CreateButton() override {
return std::make_unique<MacOSButton>();
}
};
5.2 游戏开发中的敌人生成
在游戏开发中,工厂方法模式常用于生成不同类型的敌人:
cpp复制class Enemy {
public:
virtual ~Enemy() {}
virtual void Attack() = 0;
virtual int GetHealth() = 0;
};
class Zombie : public Enemy {
int health = 100;
public:
void Attack() override { /* 僵尸攻击逻辑 */ }
int GetHealth() override { return health; }
};
class Skeleton : public Enemy {
int health = 80;
public:
void Attack() override { /* 骷髅攻击逻辑 */ }
int GetHealth() override { return health; }
};
class EnemySpawner {
public:
virtual std::unique_ptr<Enemy> SpawnEnemy() = 0;
};
class ZombieSpawner : public EnemySpawner {
public:
std::unique_ptr<Enemy> SpawnEnemy() override {
return std::make_unique<Zombie>();
}
};
class SkeletonSpawner : public EnemySpawner {
public:
std::unique_ptr<Enemy> SpawnEnemy() override {
return std::make_unique<Skeleton>();
}
};
6. 工厂方法模式与其他设计模式的关系
6.1 与抽象工厂模式的关系
工厂方法模式通常作为抽象工厂模式的一部分。抽象工厂模式中的每个方法通常都是一个工厂方法。主要区别在于:
- 工厂方法模式:创建一个产品
- 抽象工厂模式:创建一系列相关产品
6.2 与模板方法模式的关系
工厂方法模式经常与模板方法模式一起使用。模板方法模式定义了算法的骨架,将某些步骤延迟到子类中实现,而工厂方法模式就是这些步骤之一。
6.3 与单例模式的关系
在某些情况下,工厂方法可能返回同一个对象(单例),但这并不是工厂方法模式的典型用法。工厂方法通常用于创建新实例。
7. C++特定实现注意事项
7.1 内存管理
在C++中实现工厂方法模式时,需要特别注意内存管理:
- 明确所有权:工厂方法返回的指针所有权应该清晰定义
- 使用智能指针:推荐使用std::unique_ptr或std::shared_ptr
- 防止内存泄漏:确保所有创建的对象都能被正确释放
7.2 异常安全
工厂方法应该考虑异常安全:
cpp复制std::unique_ptr<Product> SafeFactoryMethod() {
auto rawPtr = new (std::nothrow) ConcreteProduct();
if (!rawPtr) {
throw std::bad_alloc();
}
return std::unique_ptr<Product>(rawPtr);
}
7.3 性能考虑
在性能敏感的场景中,可以考虑:
- 对象池技术:预先创建对象并重复使用
- 移动语义:使用移动构造函数减少拷贝开销
- 内联小型工厂方法
8. 现代C++中的工厂方法模式演进
8.1 使用lambda表达式作为工厂
C++11引入的lambda表达式可以作为轻量级工厂:
cpp复制auto productFactory = []() -> std::unique_ptr<Product> {
return std::make_unique<ConcreteProductA>();
};
auto product = productFactory();
8.2 可变参数工厂
利用可变参数模板,可以创建更灵活的工厂:
cpp复制template <typename ProductType, typename... Args>
std::unique_ptr<Product> CreateProduct(Args&&... args) {
return std::make_unique<ProductType>(std::forward<Args>(args)...);
}
8.3 使用std::function作为工厂接口
cpp复制using ProductFactory = std::function<std::unique_ptr<Product>()>;
ProductFactory factoryA = []() { return std::make_unique<ConcreteProductA>(); };
ProductFactory factoryB = []() { return std::make_unique<ConcreteProductB>(); };
auto product = factoryA();
9. 测试工厂方法模式
为工厂方法模式编写单元测试时,需要注意:
- 测试每个具体工厂是否能正确创建预期类型的产品
- 测试创建的产品是否实现了正确的接口
- 测试异常情况(如内存不足)
- 考虑使用mock对象进行测试
示例测试用例:
cpp复制TEST(FactoryMethodTest, CreatesCorrectProductType) {
ConcreteCreatorA creator;
auto product = creator.FactoryMethod();
EXPECT_NE(dynamic_cast<ConcreteProductA*>(product.get()), nullptr);
}
TEST(FactoryMethodTest, ProductImplementsInterface) {
ConcreteCreatorB creator;
auto product = creator.FactoryMethod();
EXPECT_NO_THROW(product->Operation());
}
10. 工厂方法模式的替代方案
虽然工厂方法模式很强大,但在某些情况下可能有更好的选择:
- 简单工厂:当产品类型不多且不太可能变化时
- 依赖注入:在复杂系统中管理对象创建
- 原型模式:当需要基于现有对象创建新对象时
- 构建器模式:当对象创建过程复杂且需要分步进行时
选择哪种模式取决于具体需求、系统复杂度和预期的变化点。
