You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中实现支持动态添加动物类的自动键值工厂模式?

实现可扩展的动物领养工厂(无分支判断,支持运行时新增类型)

刚好我之前做过类似的动态工厂需求,完美匹配你的场景!咱们不用写一堆if/else或者switch来判断动物类型,而是用动态注册表+函数对象的方式,让Shelter能随时接收新的Animal子类,还能安全创建实例。

核心思路

咱们搞一个「动物类型注册表」,把动物的名称字符串(比如"parrot")和对应的实例创建函数绑定起来。Shelter只需要维护这个注册表,创建动物时直接根据名称查表调用创建函数就行。开发者要加新动物,只需要调用addAnimal把类型和创建逻辑注册进去,完全不用改原有代码。


第一步:定义基础类结构

首先得把Animal基类、物品类(如果要类型安全的话)还有各个动物子类先定义好。这里我优化了物品的传递方式,用std::vector<Item*>替代void*,避免裸指针的不安全问题;如果坚持要用void*,后面也可以调整。

1. 物品基类(可选,但更安全)

如果所有物品都有共性,可以抽象成基类:

class Item {
public:
    virtual ~Item() = default;
    class Owner* owner; // 对应你说的Owner引用,供动物识别主人
};

// 具体物品类
class Collar : public Item {};
class Leash : public Item {};
class CatTree : public Item {};
class LitterBox : public Item {};
class Cage : public Item {};

2. Animal基类

class Animal {
public:
    virtual ~Animal() = default;
    // 动物通用方法,比如makeSound()
    virtual void makeSound() = 0;
};

3. 具体动物子类

每个子类的构造函数接收物品列表,并且可以加静态方法验证物品是否符合要求:

class Dog : public Animal {
public:
    // 构造函数:接收物品列表
    Dog(std::vector<Item*> items) : items_(std::move(items)) {}
    
    // 验证物品是否符合Dog的必需要求(项圈+牵引绳,无冗余)
    static bool validateItems(const std::vector<Item*>& items) {
        bool hasCollar = false, hasLeash = false;
        for (auto item : items) {
            if (dynamic_cast<Collar*>(item)) hasCollar = true;
            else if (dynamic_cast<Leash*>(item)) hasLeash = true;
            else return false; // 有冗余物品,直接返回false
        }
        return hasCollar && hasLeash;
    }
    
    void makeSound() override {
        std::cout << "Woof!" << std::endl;
    }

private:
    std::vector<Item*> items_;
};

class Cat : public Animal {
public:
    Cat(std::vector<Item*> items) : items_(std::move(items)) {}
    
    static bool validateItems(const std::vector<Item*>& items) {
        bool hasTree = false, hasLitter = false;
        for (auto item : items) {
            if (dynamic_cast<CatTree*>(item)) hasTree = true;
            else if (dynamic_cast<LitterBox*>(item)) hasLitter = true;
            else return false;
        }
        return hasTree && hasLitter;
    }
    
    void makeSound() override {
        std::cout << "Meow!" << std::endl;
    }

private:
    std::vector<Item*> items_;
};

class Parrot : public Animal {
public:
    Parrot(std::vector<Item*> items) : items_(std::move(items)) {}
    
    static bool validateItems(const std::vector<Item*>& items) {
        if (items.size() != 1 || !dynamic_cast<Cage*>(items[0])) {
            return false;
        }
        return true;
    }
    
    void makeSound() override {
        std::cout << "Polly wants a cracker!" << std::endl;
    }

private:
    std::vector<Item*> items_;
};

第二步:实现Shelter动态工厂

Shelter作为管理器,里面维护一个注册表,提供addAnimal注册新类型,getAnimal创建实例的方法:

#include <unordered_map>
#include <functional>
#include <stdexcept>
#include <vector>

class Shelter {
public:
    // 单例模式(可选,方便全局访问)
    static Shelter& getInstance() {
        static Shelter instance;
        return instance;
    }
    
    // 注册新的动物类型:key是名称,creator是创建实例的函数
    template<typename AnimalType>
    void addAnimal(const std::string& key) {
        // 把创建逻辑和验证逻辑封装成函数对象
        registry_[key] = [](std::vector<Item*> items) -> Animal* {
            // 先验证物品是否符合要求
            if (!AnimalType::validateItems(items)) {
                throw std::invalid_argument("Invalid items for " + key);
            }
            // 创建实例
            return new AnimalType(std::move(items));
        };
    }
    
    // 根据名称创建动物实例
    Animal* getAnimal(const std::string& key, std::vector<Item*> items) {
        auto it = registry_.find(key);
        if (it == registry_.end()) {
            throw std::invalid_argument("Unknown animal type: " + key);
        }
        // 调用注册的创建函数
        return it->second(std::move(items));
    }

private:
    // 注册表:键=动物名称,值=创建函数
    using CreatorFunc = std::function<Animal*(std::vector<Item*>)>;
    std::unordered_map<std::string, CreatorFunc> registry_;
    
    // 私有构造函数,禁止外部实例化(单例用)
    Shelter() = default;
    ~Shelter() = default;
    Shelter(const Shelter&) = delete;
    Shelter& operator=(const Shelter&) = delete;
};

第三步:使用示例(Owner类调用)

现在Owner类可以直接调用Shelter的方法来领养动物,完全不用关心具体的类型判断:

class Owner {
public:
    void adoptAnimal(const std::string& animalType) {
        // 准备对应动物的必需物品,注意设置owner引用
        std::vector<Item*> items;
        if (animalType == "dog") {
            auto collar = new Collar();
            collar->owner = this;
            auto leash = new Leash();
            leash->owner = this;
            items = {collar, leash};
        } else if (animalType == "cat") {
            auto tree = new CatTree();
            tree->owner = this;
            auto litter = new LitterBox();
            litter->owner = this;
            items = {tree, litter};
        } else if (animalType == "parrot") {
            auto cage = new Cage();
            cage->owner = this;
            items = {cage};
        }
        
        try {
            Animal* pet = Shelter::getInstance().getAnimal(animalType, items);
            pet->makeSound();
            // 后续可以把pet存到Owner的宠物列表里
        } catch (const std::invalid_argument& e) {
            std::cerr << "Adoption failed: " << e.what() << std::endl;
            // 清理物品
            for (auto item : items) delete item;
        }
    }
};

// 主函数测试
int main() {
    // 先注册所有支持的动物类型
    Shelter::getInstance().addAnimal<Dog>("dog");
    Shelter::getInstance().addAnimal<Cat>("cat");
    Shelter::getInstance().addAnimal<Parrot>("parrot");
    
    // Owner领养动物
    Owner me;
    me.adoptAnimal("parrot"); // 输出:Polly wants a cracker!
    me.adoptAnimal("dog");    // 输出:Woof!
    
    // 运行时新增动物类型(比如Hamster)
    class Hamster : public Animal {
    public:
        Hamster(std::vector<Item*> items) : items_(std::move(items)) {}
        
        static bool validateItems(const std::vector<Item*>& items) {
            // 假设Hamster需要小笼子
            class HamsterCage : public Item {};
            return items.size() == 1 && dynamic_cast<HamsterCage*>(items[0]);
        }
        
        void makeSound() override {
            std::cout << "Squeak!" << std::endl;
        }
        
    private:
        std::vector<Item*> items_;
    };
    
    Shelter::getInstance().addAnimal<Hamster>("hamster");
    // 准备Hamster的物品
    Owner kid;
    auto hamsterCage = new Hamster::HamsterCage();
    hamsterCage->owner = &kid;
    Animal* hamster = Shelter::getInstance().getAnimal("hamster", {hamsterCage});
    hamster->makeSound(); // 输出:Squeak!
    
    // 记得清理内存(实际项目建议用智能指针)
    delete hamster;
    delete hamsterCage;
    
    return 0;
}

关键细节说明

  1. 类型安全优化:我用std::vector<Item*>替代了void*,避免了裸指针的类型不安全问题;如果你的项目必须用void*,可以把Shelter里的CreatorFunc改成std::function<Animal*(void*, int)>,然后在创建函数里把void*转换成对应的数组类型。
  2. 物品验证:每个动物子类的validateItems静态方法确保物品是必需的且无冗余,不符合要求会抛出异常,避免创建出物品不全的动物。
  3. 运行时扩展:开发者只需要定义新的Animal子类,调用addAnimal<NewAnimal>("new_animal")就能注册,完全不用修改Shelter的原有代码,符合开闭原则。
  4. 智能指针建议:实际项目里建议用std::unique_ptr<Animal>替代裸指针,避免内存泄漏,比如把getAnimal的返回值改成std::unique_ptr<Animal>,创建函数里返回std::make_unique<AnimalType>(...)。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:57:14