You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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;
}

代码里的核心问题&优化方向

你的代码已经摸到了抽象工厂的门槛,但还有几个关键问题不符合模式的设计意图,也不太适合游戏引擎这类大型项目的开发规范:

  1. 产品族逻辑错误OrcFactoryHumanFactory居然返回完全一样的ArcherRider!抽象工厂的核心是每个具体工厂生产属于自己产品线的专属产品——兽人弓箭手应该是OrcArcher,人类弓箭手是HumanArcher,这样才能体现“一族相关产品”的设计目的。
  2. 内存泄漏风险:你用new创建了一堆对象,但完全没有delete操作,在C里这会导致内存泄漏。游戏引擎对内存管理要求极高,现代C推荐用智能指针自动管理内存。
  3. 违背封装性的类型转换main里用static_castSoldier*转成具体子类指针,这等于绕开了抽象工厂的封装意义——抽象工厂就是要让客户端只依赖抽象类型,不需要知道具体子类细节。
  4. 访问权限错误OrcFactoryHumanFactory里的创建函数是私有成员!基类的纯虚函数是public,子类重写的函数默认是private,这会导致外部根本调用不了这些创建方法,编译直接报错。
  5. 不规范的命名空间使用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_ptrstd::make_unique自动管理对象生命周期,彻底避免内存泄漏,这在游戏引擎这类需要长期运行的程序里至关重要。
  • 强化封装性Game类和客户端(main)只依赖抽象的SoldierAbstractSoldierFactory,不需要知道具体子类,符合依赖倒置原则,也方便后续扩展新阵营(比如精灵、矮人)。
  • 修正访问权限:把具体工厂的创建函数设为public,确保外部可以正常调用。
  • 规范命名空间:移除全局using namespace std;,改用std::前缀,避免命名冲突。

游戏引擎场景下的延伸思考

在游戏引擎架构里,抽象工厂模式常用来处理不同平台的资源创建(比如Windows/Console的渲染资源)、不同游戏模式的实体生成(比如单人/多人模式的玩家角色)。你可以把这个思路扩展到更多产品类型,比如新增WeaponVehicle等抽象基类,让每个具体工厂负责创建一整套对应阵营的产品。

如果需要动态切换阵营(比如游戏中途切换种族),还可以把工厂的创建逻辑封装到一个工厂方法或单例中,方便动态替换。

内容的提问来源于stack exchange,提问作者Gustavo Otero

火山引擎 最新活动