如何借助模板基类消除工厂类中派生类的冗余代码
解决方案:类型擦除+模板注册实现通用抽象工厂
这个场景我太熟悉了——要做通用抽象工厂,但两个产品族根本没共同基类,模板虚函数又行不通,重复写createA、createB实在太冗余。别担心,咱们用类型擦除+模板注册机制就能完美实现你想要的统一create方法,而且代码复用性拉满。
核心思路
我们的目标是:
- 让
factories类不用关心具体产品类型,通过模板推导自动匹配对应的创建逻辑 - 用类型擦除(
std::function+void*)绕过A、B无共同基类的限制 - 把每个产品族的创建逻辑单独注册到
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; }
方案优势
- 完全消除重复代码:不用再写
createA、createB,所有产品族共用同一个create方法 - 无侵入性:不需要修改原有A、B类族的结构,不用给它们加共同基类
- 扩展性强:新增产品族C时,只需要写对应的创建逻辑并注册,完全不用修改
factories类 - 类型安全:模板推导和
dynamic_cast保证了类型安全,运行时错误会抛出异常
内容的提问来源于stack exchange,提问作者Andre Marin
相关产品推荐
相关产品推荐

