直接 new 对象导致维护困难,因具体类型被硬编码,更换实现需全局修改;工厂模式解耦“谁来造”和“造什么”,调用方仅依赖稳定接口。

为什么直接 new 一个对象会让后续维护变困难
当你在业务逻辑里到处写 new ConcreteProduct(),等于把具体类型硬编码进调用方。一旦要换实现(比如从 FileLogger 切到 NetworkLogger),就得翻遍所有 new 地方改——漏一处就崩。
工厂模式的核心不是“多写几个类”,而是把“谁来造”和“造什么”拆开。调用方只认接口,不关心构造细节。
- 接口定义要稳定:
class Product { public: virtual void operate() = 0; virtual ~Product() = default; }; - 具体类只负责实现,不暴露构造逻辑:
class ConcreteProductA : public Product { void operate() override { /*...*/ } }; - 工厂类本身不持有状态,通常设计为静态方法或单例,避免引入额外生命周期管理负担
静态工厂函数 vs 抽象工厂类:什么时候该选哪个
静态工厂函数适合类型不多、创建逻辑简单的情况;抽象工厂类则用于需要隔离不同产品族(比如 WindowsUIFactory / MacUIFactory 各自产出 Button 和 Checkbox)。
别一上来就搞抽象工厂——90% 的场景,一个带 switch 或 if-else 的静态 createProduct() 就够用,还容易测试。
立即学习“C++免费学习笔记(深入)”;
- 静态工厂示例:
class ProductFactory { public: static std::unique_ptr<Product> create(const std::string& type) { if (type == "A") return std::make_unique<ConcreteProductA>(); if (type == "B") return std::make_unique<ConcreteProductB>(); throw std::invalid_argument("Unknown product type: " + type); } }; - 抽象工厂要多一层继承层级,且每个产品族都要实现全套接口,代码量翻倍,仅当明确需要支持多套 UI/驱动/协议时才值得投入
- 注意返回类型:用
std::unique_ptr<product></product>而不是裸指针,避免内存泄漏;若需共享所有权,再考虑std::shared_ptr
传参方式决定扩展性:用字符串还是枚举控制创建行为
用 std::string 当参数看着灵活,但拼错就 runtime crash;用枚举安全,但每次加类型都要改头文件、重编译。
实际项目里更推荐“枚举 + 字符串 fallback”的混合策略:内部用枚举分发,对外提供 fromString() 做校验转换。
- 枚举定义:
enum class ProductType { A, B, C }; - 字符串转枚举函数必须做边界检查,不能直接
static_cast,否则越界访问未定义行为 - 如果配置来自 JSON/YAML,先解析成字符串,再调用转换函数,别让解析层直接耦合具体产品类型
- 避免在工厂内部做复杂初始化(如读配置文件、连数据库),那属于产品类自己的责任,工厂只管 new 出来
std::shared_ptr 比 std::unique_ptr 更危险的两个地方
很多人觉得 shared_ptr 自动管理更省心,但在工厂模式里它反而更容易埋雷。
- 循环引用:如果
Product内部又存了工厂的shared_ptr(比如回调注册),对象永远不会析构 - 性能开销:每次拷贝
shared_ptr都要原子增减引用计数,高频创建场景下比unique_ptr明显慢 - 除非明确需要跨线程共享所有权,或者产品对象生命周期必须脱离调用栈存在(比如扔进异步队列),否则默认用
unique_ptr更安全、更轻量
工厂返回 unique_ptr 不代表调用方不能转成 shared_ptr——那是使用者的自由,工厂不该越界干涉。











