C++中虚类实现工厂方法相较于函数映射的必要性及后者的潜在缺陷
嘿,这个问题问得挺实在的——我之前在项目里也纠结过这两种工厂实现方式,咱们来好好聊聊第二种用std::function映射的潜在缺陷,以及什么时候传统虚类工厂更有必要:
首先先把你提到的两种实现方式的代码整理出来,方便对照:
传统虚类工厂实现
struct Base { virtual void foo(/*...*/) = 0; virtual ~Base() = default; }; struct A: Base { void foo(/*...*/) override; }; struct BaseFactory { virtual std::unique_ptr<Base> create(/*...*/) = 0; }; struct AFactory : BaseFactory { std::unique_ptr<Base> create(/*...*/) override; // 返回指向A的指针 }; std::map<std::string, std::unique_ptr<BaseFactory>> factories; // 这个map会在某处初始化,不是问题重点
std::function映射实现
std::map<std::string, std::function<std::unique_ptr<Base>(/*...*/)>> factories;
接下来聊聊第二种方式的几个核心问题:
无法维护工厂状态:如果你的工厂需要持有一些持久化的状态(比如创建对象时复用的资源、全局配置参数,或者统计创建次数的计数器),
std::function就很难优雅处理。虚类工厂可以把这些状态作为成员变量封装在工厂类里,而std::function绑定的要么是无状态的函数/lambda,要么得额外绑定外部变量,这会让状态分散在代码各处,既不封装也容易出bug。扩展性严重受限:你现在觉得工厂的唯一作用就是创建对象,但工程里需求从来不是一成不变的。比如后续要给工厂加个
validateCreationParams()方法检查参数合法性,或者加个getCreationStats()统计创建信息,虚类工厂只需要在BaseFactory里加纯虚函数,子类实现就行。但std::function的映射表只能存创建函数,没法扩展其他接口,到时候要么被迫重构回虚类,要么搞一堆额外的映射表,代码会变得杂乱无章。类型安全与可读性不足:虚类工厂有清晰的继承体系,所有工厂都继承自
BaseFactory,类型明确,别人看代码一眼就知道这是负责创建Base子类的工厂。而std::function映射表虽然灵活,但如果绑定的函数签名不对,编译错误会非常晦涩;另外维护代码时,得去逐个查每个字符串对应的创建逻辑到底是什么,不像虚类那样有直观的类结构可以追溯。动态替换的便利性差:比如测试场景下,你需要用Mock工厂替换真实工厂来做单元测试。虚类工厂只需要替换
factories里的BaseFactory子类实例就行,逻辑集中清晰。但std::function的话,你得去修改映射表中对应的条目,如果代码中有多处依赖这个映射表,很容易漏改,维护成本更高。
当然,也得说句公道话:如果你的场景真的极端简单——工厂完全无状态,而且永远只需要创建对象这一个功能,那std::function映射确实更简洁,少写一堆冗余的工厂类。但绝大多数工程场景中,需求都会演化,提前用虚类工厂其实是给未来留了足够的扩展空间,避免后期重构的麻烦。
内容来源于stack exchange

