1. 工厂方法模式概述
工厂方法模式(Factory Method Pattern)是面向对象设计中最常用的创建型模式之一。在C++开发中,它通过定义一个创建对象的接口,但让子类决定实例化哪个类,完美解决了对象创建与使用的耦合问题。
我第一次在游戏引擎开发中接触这个模式时,它帮我解决了角色创建系统的扩展难题。当时需要支持多种角色类型(战士、法师、弓箭手等),如果直接用new操作符创建对象,每次新增角色类型都要修改核心代码。而工厂方法让新增角色类型就像添加新子类一样简单。
这个模式的核心价值在于:
- 将对象创建延迟到子类
- 提供统一的创建接口
- 支持开闭原则(对扩展开放,对修改关闭)
注意:工厂方法不同于简单工厂模式。简单工厂把创建逻辑集中在一个工厂类中,而工厂方法将创建责任分散到各个子工厂。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 模式结构与实现原理
2.1 UML类图解析
典型的工厂方法模式包含以下角色:
- Product(抽象产品):定义产品的接口
- ConcreteProduct(具体产品):实现Product接口
- Creator(抽象工厂):声明工厂方法
- ConcreteCreator(具体工厂):重写工厂方法
cpp复制// 抽象产品
class Weapon {
public:
virtual ~Weapon() = default;
virtual void attack() = 0;
};
// 具体产品
class Sword : public Weapon {
public:
void attack() override {
std::cout << "Sword slashing!" << std::endl;
}
};
class Bow : public Weapon {
public:
void attack() override {
std::cout << "Bow shooting!" << std::endl;
}
};
// 抽象工厂
class WeaponFactory {
public:
virtual ~WeaponFactory() = default;
virtual std::unique_ptr<Weapon> createWeapon() = 0;
};
// 具体工厂
class SwordFactory : public WeaponFactory {
public:
std::unique_ptr<Weapon> createWeapon() override {
return std::make_unique<Sword>();
}
};
class BowFactory : public WeaponFactory {
public:
std::unique_ptr<Weapon> createWeapon() override {
return std::make_unique<Bow>();
}
};
2.2 创建过程的运行时动态性
工厂方法的核心优势在于运行时动态创建对象。通过多态机制,客户端代码只需与抽象接口交互:
cpp复制void equipWeapon(WeaponFactory& factory) {
auto weapon = factory.createWeapon();
weapon->attack();
}
int main() {
SwordFactory swordFactory;
BowFactory bowFactory;
equipWeapon(swordFactory); // 输出: Sword slashing!
equipWeapon(bowFactory); // 输出: Bow shooting!
}
这种设计使得新增武器类型时,只需添加新的具体产品和工厂类,无需修改现有客户端代码。
3. 高级应用技巧
3.1 模板工厂方法
对于产品类型已知且固定的场景,可以使用模板减少工厂子类数量:
cpp复制template<typename T>
class GenericWeaponFactory : public WeaponFactory {
public:
std::unique_ptr<Weapon> createWeapon() override {
return std::make_unique<T>();
}
};
// 使用示例
GenericWeaponFactory<Sword> swordFactory;
auto sword = swordFactory.createWeapon();
3.2 带参数的工厂方法
有时创建对象需要额外参数,可以扩展工厂方法接口:
cpp复制class WeaponFactory {
public:
virtual std::unique_ptr<Weapon> createWeapon(int damage) = 0;
};
class SwordFactory : public WeaponFactory {
public:
std::unique_ptr<Weapon> createWeapon(int damage) override {
auto sword = std::make_unique<Sword>();
sword->setDamage(damage);
return sword;
}
};
3.3 工厂方法与单例模式结合
当工厂本身需要全局唯一时:
cpp复制class SwordFactory : public WeaponFactory {
private:
SwordFactory() = default;
public:
static SwordFactory& getInstance() {
static SwordFactory instance;
return instance;
}
std::unique_ptr<Weapon> createWeapon() override {
return std::make_unique<Sword>();
}
};
4. 实际应用场景分析
4.1 游戏开发中的资源管理
在游戏引擎中,工厂方法常用于管理不同类型的游戏资源:
cpp复制class TextureFactory : public ResourceFactory {
public:
std::unique_ptr<Resource> create() override {
return std::make_unique<Texture>();
}
};
class SoundFactory : public ResourceFactory {
public:
std::unique_ptr<Resource> create() override {
return std::make_unique<Sound>();
}
};
4.2 跨平台UI组件创建
开发跨平台应用时,为不同操作系统创建对应的UI控件:
cpp复制class ButtonFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
};
class WinButtonFactory : public ButtonFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WinButton>();
}
};
class MacButtonFactory : public ButtonFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacButton>();
}
};
4.3 插件系统架构
工厂方法是插件系统的理想选择,主程序通过接口与插件交互:
cpp复制// 插件接口
class Plugin {
public:
virtual void execute() = 0;
};
// 插件工厂接口
class PluginFactory {
public:
virtual std::unique_ptr<Plugin> createPlugin() = 0;
};
// 具体插件实现
class DemoPlugin : public Plugin {
public:
void execute() override {
std::cout << "DemoPlugin running" << std::endl;
}
};
// 具体插件工厂
class DemoPluginFactory : public PluginFactory {
public:
std::unique_ptr<Plugin> createPlugin() override {
return std::make_unique<DemoPlugin>();
}
};
5. 性能优化与最佳实践
5.1 对象池与工厂方法结合
频繁创建销毁对象时,使用对象池提升性能:
cpp复制class WeaponPool {
private:
std::vector<std::unique_ptr<Weapon>> pool;
WeaponFactory& factory;
public:
WeaponPool(WeaponFactory& f, size_t initialSize)
: factory(f) {
for(size_t i=0; i<initialSize; ++i) {
pool.push_back(factory.createWeapon());
}
}
std::unique_ptr<Weapon> acquire() {
if(pool.empty()) {
return factory.createWeapon();
}
auto obj = std::move(pool.back());
pool.pop_back();
return obj;
}
void release(std::unique_ptr<Weapon> weapon) {
pool.push_back(std::move(weapon));
}
};
5.2 现代C++特性应用
使用智能指针和移动语义优化资源管理:
cpp复制class WeaponFactory {
public:
virtual std::unique_ptr<Weapon> createWeapon() = 0;
// 批量创建
std::vector<std::unique_ptr<Weapon>> createWeapons(size_t count) {
std::vector<std::unique_ptr<Weapon>> weapons;
weapons.reserve(count);
for(size_t i=0; i<count; ++i) {
weapons.push_back(createWeapon());
}
return weapons;
}
};
5.3 工厂方法模式与其它模式的对比
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 简单工厂 | 集中创建逻辑,违反开闭原则 | 对象创建逻辑简单 |
| 工厂方法 | 分散创建责任,支持扩展 | 需要灵活扩展产品类型 |
| 抽象工厂 | 创建产品族 | 需要创建相关对象系列 |
6. 常见问题与解决方案
6.1 循环依赖问题
当工厂和产品相互引用时可能导致编译问题。解决方案:
- 使用前向声明
- 将工厂方法实现移到cpp文件
- 使用接口隔离
cpp复制// Weapon.h
class WeaponFactory; // 前向声明
class Weapon {
public:
virtual void configure(WeaponFactory& factory) = 0;
};
// WeaponFactory.h
class Weapon; // 前向声明
class WeaponFactory {
public:
virtual std::unique_ptr<Weapon> createWeapon() = 0;
};
6.2 动态注册工厂
实现运行时注册工厂的机制:
cpp复制class WeaponFactoryRegistry {
private:
std::unordered_map<std::string, std::function<std::unique_ptr<Weapon>()>> factories;
public:
static WeaponFactoryRegistry& instance() {
static WeaponFactoryRegistry reg;
return reg;
}
void registerFactory(const std::string& type, auto factory) {
factories[type] = factory;
}
std::unique_ptr<Weapon> create(const std::string& type) {
if(auto it = factories.find(type); it != factories.end()) {
return it->second();
}
throw std::runtime_error("Unknown weapon type");
}
};
// 注册示例
WeaponFactoryRegistry::instance().registerFactory("sword", []{
return std::make_unique<Sword>();
});
6.3 单元测试中的模拟
使用工厂方法可以方便地进行单元测试:
cpp复制class MockWeapon : public Weapon {
public:
MOCK_METHOD(void, attack, (), (override));
};
class MockWeaponFactory : public WeaponFactory {
public:
MOCK_METHOD(std::unique_ptr<Weapon>, createWeapon, (), (override));
};
TEST(WeaponTest, AttackTest) {
MockWeaponFactory factory;
EXPECT_CALL(factory, createWeapon())
.WillOnce([]() { return std::make_unique<MockWeapon>(); });
auto weapon = factory.createWeapon();
EXPECT_CALL(dynamic_cast<MockWeapon&>(*weapon), attack());
weapon->attack();
}
7. 现代C++中的演进
7.1 使用std::function作为工厂
C++11后可以使用函数对象替代抽象工厂类:
cpp复制using WeaponFactory = std::function<std::unique_ptr<Weapon>()>;
WeaponFactory swordFactory = [] {
return std::make_unique<Sword>();
};
auto sword = swordFactory();
7.2 可变参数工厂
支持任意参数的通用工厂:
cpp复制template<typename Product, typename... Args>
class GenericFactory {
public:
using ProductPtr = std::unique_ptr<Product>;
using CreateFunc = std::function<ProductPtr(Args...)>;
private:
CreateFunc creator;
public:
explicit GenericFactory(CreateFunc f) : creator(f) {}
ProductPtr create(Args... args) {
return creator(std::forward<Args>(args)...);
}
};
// 使用示例
GenericFactory<Weapon, int> factory([](int damage) {
auto sword = std::make_unique<Sword>();
sword->setDamage(damage);
return sword;
});
auto weapon = factory.create(100);
7.3 结合概念(Concepts)的编译时检查
C++20引入的概念可以增强工厂接口的编译时检查:
cpp复制template<typename T>
concept WeaponFactory = requires(T f) {
{ f.createWeapon() } -> std::convertible_to<std::unique_ptr<Weapon>>;
};
template<WeaponFactory Factory>
void combatSimulation(Factory& factory) {
auto weapon = factory.createWeapon();
weapon->attack();
}
8. 设计考量与替代方案
8.1 何时不使用工厂方法
虽然工厂方法很强大,但并非万能。以下情况可能需要考虑其他方案:
- 对象创建逻辑极其简单时(直接构造可能更清晰)
- 性能敏感场景(虚函数调用有开销)
- 需要创建的对象类型在编译时完全确定
8.2 与依赖注入的结合
现代框架常将工厂方法与依赖注入容器结合:
cpp复制class WeaponService {
private:
WeaponFactory& factory;
public:
explicit WeaponService(WeaponFactory& f) : factory(f) {}
void prepareForBattle() {
auto weapon = factory.createWeapon();
weapon->attack();
}
};
8.3 原型模式作为替代
当对象创建成本高时,可以考虑原型模式:
cpp复制class Weapon {
public:
virtual std::unique_ptr<Weapon> clone() = 0;
};
class Sword : public Weapon {
public:
std::unique_ptr<Weapon> clone() override {
return std::make_unique<Sword>(*this);
}
};
// 使用原型替代工厂
Sword prototypeSword;
auto newSword = prototypeSword.clone();
在实际项目中,我经常发现开发者过度设计工厂层次结构。一个经验法则是:只有当确实需要支持多种产品类型,且这些类型可能在将来扩展时,才值得引入工厂方法模式。对于简单场景,直接构造往往更易于维护。
