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

如何借助模板基类消除工厂类中派生类的冗余代码

解决方案:类型擦除+模板注册实现通用抽象工厂

这个场景我太熟悉了——要做通用抽象工厂,但两个产品族根本没共同基类,模板虚函数又行不通,重复写createA、createB实在太冗余。别担心,咱们用类型擦除+模板注册机制就能完美实现你想要的统一create方法,而且代码复用性拉满。

核心思路

我们的目标是:

  1. 让factories类不用关心具体产品类型,通过模板推导自动匹配对应的创建逻辑
  2. 用类型擦除(std::function+void*)绕过A、B无共同基类的限制
  3. 把每个产品族的创建逻辑单独注册到factories中,避免重复代码

完整代码实现

1. 产品类族定义(保持你的原有结构)

#include <memory>
#include <unordered_map>
#include <functional>
#include <typeindex>
#include <stdexcept>
#include <iostream>

// 产品类族A
struct A { virtual ~A() = default; };
struct derivedA1 : public A { void print() { std::cout << "I'm derivedA1\n"; } };
struct derivedA2 : public derivedA1 { void print() { std::cout << "I'm derivedA2\n"; } };

// 产品类族B
struct B { virtual ~B() = default; };
struct derivedB1 : public B { void print() { std::cout << "I'm derivedB1\n"; } };
struct derivedB2 : public derivedB1 { void print() { std::cout << "I'm derivedB2\n"; } };

2. 通用factories类实现

这个类是核心,用std::type_index区分不同产品类型,存储对应的创建函数,再通过模板create方法自动匹配:

struct factories {
private:
    // 用类型索引作为key,存储"id -> 产品指针"的创建逻辑(类型擦除)
    std::unordered_map<std::type_index, std::function<void(int, void*)>> creators_;

    // 辅助模板:安全地将创建好的智能指针赋值给输出参数
    template<typename Product>
    static void assign_ptr(std::shared_ptr<Product>& dest, std::shared_ptr<Product> src) {
        dest = std::move(src);
    }

public:
    // 注册产品族的创建逻辑:传入一个"int id -> 产品智能指针"的函数
    template<typename Product>
    void register_creator(std::function<std::shared_ptr<Product>(int)> creator) {
        creators_[std::type_index(typeid(Product))] = [creator](int id, void* ptr) {
            auto product_ptr = creator(id);
            // 把创建好的指针赋值给传入的智能指针引用
            assign_ptr(*static_cast<std::shared_ptr<Product>*>(ptr), std::move(product_ptr));
        };
    }

    // 你想要的通用create方法:通过输出参数的类型推导要创建的产品类型
    template<typename Product>
    void create(int id, std::shared_ptr<Product>& out_ptr) {
        auto type_key = std::type_index(typeid(Product));
        auto it = creators_.find(type_key);
        if (it == creators_.end()) {
            throw std::runtime_error("No creator registered for product type: " + std::string(typeid(Product).name()));
        }
        // 调用对应的创建逻辑
        it->second(id, &out_ptr);
    }
};

3. 注册产品族的工厂逻辑

我们可以把每个产品族的创建逻辑单独封装,也可以用模板特化进一步简化:

// 方式1:直接用lambda注册(灵活)
void register_all_factories(factories& f) {
    // 注册A类族的创建逻辑
    f.register_creator<A>([](int id) -> std::shared_ptr<A> {
        switch(id) {
            case 0: return std::make_shared<derivedA1>();
            case 1: return std::make_shared<derivedA2>();
            default: throw std::invalid_argument("Invalid id for A type");
        }
    });

    // 注册B类族的创建逻辑
    f.register_creator<B>([](int id) -> std::shared_ptr<B> {
        switch(id) {
            case 0: return std::make_shared<derivedB1>();
            case 1: return std::make_shared<derivedB2>();
            default: throw std::invalid_argument("Invalid id for B type");
        }
    });
}

// 方式2:用模板特化封装工厂逻辑(更规范,适合复杂场景)
template<typename BaseProduct>
struct ProductFactory {
    static std::shared_ptr<BaseProduct> create(int id) {
        throw std::runtime_error("Factory not implemented for this product type");
    }
};

// 特化A类族的工厂
template<>
struct ProductFactory<A> {
    static std::shared_ptr<A> create(int id) {
        switch(id) {
            case 0: return std::make_shared<derivedA1>();
            case 1: return std::make_shared<derivedA2>();
            default: throw std::invalid_argument("Invalid id for A");
        }
    }
};

// 特化B类族的工厂
template<>
struct ProductFactory<B> {
    static std::shared_ptr<B> create(int id) {
        switch(id) {
            case 0: return std::make_shared<derivedB1>();
            case 1: return std::make_shared<derivedB2>();
            default: throw std::invalid_argument("Invalid id for B");
        }
    }
};

4. 你的期望用法(完全匹配)

不管用哪种注册方式,最终的调用方式完全符合你的要求:

int main() {
    factories l_factories;
    // 方式1:用lambda注册
    register_all_factories(l_factories);
    // 方式2:用模板特化注册(二选一即可)
    // f.register_creator<A>(&ProductFactory<A>::create);
    // f.register_creator<B>(&ProductFactory<B>::create);

    std::shared_ptr<A> a_ptr;
    std::shared_ptr<B> b_ptr;

    // 调用统一的create方法,自动匹配产品类型
    l_factories.create(0, a_ptr); // 创建derivedA1
    l_factories.create(1, b_ptr); // 创建derivedB2

    // 验证结果
    if (auto ptr = dynamic_cast<derivedA1*>(a_ptr.get())) {
        ptr->print(); // 输出:I'm derivedA1
    }
    if (auto ptr = dynamic_cast<derivedB2*>(b_ptr.get())) {
        ptr->print(); // 输出:I'm derivedB2
    }

    return 0;
}

方案优势

  1. 完全消除重复代码:不用再写createA、createB,所有产品族共用同一个create方法
  2. 无侵入性:不需要修改原有A、B类族的结构,不用给它们加共同基类
  3. 扩展性强:新增产品族C时,只需要写对应的创建逻辑并注册,完全不用修改factories类
  4. 类型安全:模板推导和dynamic_cast保证了类型安全,运行时错误会抛出异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:23:09