C++抽象工厂模式实现疑问:我的代码是否合理?
嘿,很高兴帮你梳理这个抽象工厂模式的实现!先给你点个赞,能自己动手尝试把设计模式落地已经很棒了,尤其是在游戏引擎架构的学习场景下。咱们先看看你的代码,再聊聊可以优化的地方:
你的原始代码
#include <iostream> using namespace std; class Soldier { public: virtual void shoot()=0; }; class Archer: public Soldier { public: void shoot(){ cout<<"Archer shoot"<<endl; } }; class Rider: public Soldier { public: void shoot(){ cout<<"Rider shoot"<<endl; } }; class AbstractFactory { public: virtual Soldier* createArcher()=0; virtual Soldier* createRider()=0; }; class OrcFactory: public AbstractFactory { Soldier* createArcher() { return new Archer(); }; Soldier* createRider() { return new Rider(); }; }; class HumanFactory: public AbstractFactory { Soldier* createArcher() { return new Archer(); }; Soldier* createRider() { return new Rider(); }; }; class Game { public: AbstractFactory* factory; Game(AbstractFactory* factory):factory(factory){}; }; int main() { Game* game = new Game(new HumanFactory); Archer* HumanArcher = static_cast <Archer*>(game->factory->createArcher()); Rider* humanRider = static_cast <Rider*>(game->factory->createRider()); HumanArcher->shoot(); humanRider->shoot(); return 0; }
代码里的核心问题&优化方向
你的代码已经摸到了抽象工厂的门槛,但还有几个关键问题不符合模式的设计意图,也不太适合游戏引擎这类大型项目的开发规范:
- 产品族逻辑错误:
OrcFactory和HumanFactory居然返回完全一样的Archer和Rider!抽象工厂的核心是每个具体工厂生产属于自己产品线的专属产品——兽人弓箭手应该是OrcArcher,人类弓箭手是HumanArcher,这样才能体现“一族相关产品”的设计目的。 - 内存泄漏风险:你用
new创建了一堆对象,但完全没有delete操作,在C里这会导致内存泄漏。游戏引擎对内存管理要求极高,现代C推荐用智能指针自动管理内存。 - 违背封装性的类型转换:
main里用static_cast把Soldier*转成具体子类指针,这等于绕开了抽象工厂的封装意义——抽象工厂就是要让客户端只依赖抽象类型,不需要知道具体子类细节。 - 访问权限错误:
OrcFactory和HumanFactory里的创建函数是私有成员!基类的纯虚函数是public,子类重写的函数默认是private,这会导致外部根本调用不了这些创建方法,编译直接报错。 - 不规范的命名空间使用:
using namespace std;在大型项目(比如游戏引擎)里容易引发命名冲突,尽量避免全局使用。
改进后的代码示例
结合游戏引擎的场景,我调整了代码,解决了上面的问题,同时贴合现代C++的最佳实践:
#include <iostream> #include <memory> // 抽象产品:士兵基类,必须加虚析构确保子类析构被正确调用 class Soldier { public: virtual ~Soldier() = default; virtual void shoot() = 0; virtual void warCry() = 0; // 新增通用接口,减少对具体子类的依赖 }; // 人类产品线:专属士兵实现 class HumanArcher : public Soldier { public: void shoot() override { std::cout << "Human Archer shoots with a precision longbow!" << std::endl; } void warCry() override { std::cout << "For the Kingdom!" << std::endl; } }; class HumanRider : public Soldier { public: void shoot() override { std::cout << "Human Rider shoots while galloping on a warhorse!" << std::endl; } void warCry() override { std::cout << "Charge!" << std::endl; } }; // 兽人类产品线:专属士兵实现 class OrcArcher : public Soldier { public: void shoot() override { std::cout << "Orc Archer shoots with a crude bone bow!" << std::endl; } void warCry() override { std::cout << "For the Horde!" << std::endl; } }; class OrcRider : public Soldier { public: void shoot() override { std::cout << "Orc Rider shoots with a heavy crossbow from a wolf mount!" << std::endl; } void warCry() override { std::cout << "Kill all softskins!" << std::endl; } }; // 抽象工厂:定义产品族的创建接口 class AbstractSoldierFactory { public: virtual ~AbstractSoldierFactory() = default; virtual std::unique_ptr<Soldier> createArcher() = 0; virtual std::unique_ptr<Soldier> createRider() = 0; }; // 具体工厂:人类士兵工厂 class HumanSoldierFactory : public AbstractSoldierFactory { public: std::unique_ptr<Soldier> createArcher() override { return std::make_unique<HumanArcher>(); } std::unique_ptr<Soldier> createRider() override { return std::make_unique<HumanRider>(); } }; // 具体工厂:兽人士兵工厂 class OrcSoldierFactory : public AbstractSoldierFactory { public: std::unique_ptr<Soldier> createArcher() override { return std::make_unique<OrcArcher>(); } std::unique_ptr<Soldier> createRider() override { return std::make_unique<OrcRider>(); } }; // 游戏类:仅依赖抽象类型,完全不关心具体实现 class Game { private: std::unique_ptr<AbstractSoldierFactory> factory; public: explicit Game(std::unique_ptr<AbstractSoldierFactory> factory) : factory(std::move(factory)) {} void spawnAndDeploySoldiers() { // 客户端只和抽象Soldier交互,不需要知道具体种族 auto archer = factory->createArcher(); auto rider = factory->createRider(); archer->shoot(); archer->warCry(); rider->shoot(); rider->warCry(); } }; int main() { // 创建人类阵营游戏 std::cout << "=== Human Army Deployment ===" << std::endl; Game humanGame(std::make_unique<HumanSoldierFactory>()); humanGame.spawnAndDeploySoldiers(); // 创建兽人阵营游戏 std::cout << "\n=== Orc Army Deployment ===" << std::endl; Game orcGame(std::make_unique<OrcSoldierFactory>()); orcGame.spawnAndDeploySoldiers(); // 智能指针自动释放内存,无需手动delete return 0; }
关键改进的说明
- 完善产品族:每个具体工厂生产对应种族的专属士兵,完全符合抽象工厂“生产一族相关产品”的核心设计,也更贴合游戏里不同阵营的设定。
- 智能指针管理内存:用
std::unique_ptr和std::make_unique自动管理对象生命周期,彻底避免内存泄漏,这在游戏引擎这类需要长期运行的程序里至关重要。 - 强化封装性:
Game类和客户端(main)只依赖抽象的Soldier和AbstractSoldierFactory,不需要知道具体子类,符合依赖倒置原则,也方便后续扩展新阵营(比如精灵、矮人)。 - 修正访问权限:把具体工厂的创建函数设为
public,确保外部可以正常调用。 - 规范命名空间:移除全局
using namespace std;,改用std::前缀,避免命名冲突。
游戏引擎场景下的延伸思考
在游戏引擎架构里,抽象工厂模式常用来处理不同平台的资源创建(比如Windows/Console的渲染资源)、不同游戏模式的实体生成(比如单人/多人模式的玩家角色)。你可以把这个思路扩展到更多产品类型,比如新增Weapon、Vehicle等抽象基类,让每个具体工厂负责创建一整套对应阵营的产品。
如果需要动态切换阵营(比如游戏中途切换种族),还可以把工厂的创建逻辑封装到一个工厂方法或单例中,方便动态替换。
内容的提问来源于stack exchange,提问作者Gustavo Otero




