应使用工厂模式而非直接 new:因构造逻辑复杂或需解耦多实现,工厂将创建决策集中化;简单工厂用参数控制返回类型,须注意虚析构与智能指针;工厂方法让子类决定实例化;抽象工厂则确保一族相关对象的一致性创建。

为什么不用 new 直接创建对象而要用工厂
当对象的构造逻辑复杂(比如需要读配置、校验参数、初始化依赖),或同一接口有多个实现(如 Logger 有 FileLogger、ConsoleLogger、NetworkLogger),硬编码 new FileLogger() 会让调用方和具体类型强耦合,后续加新类型就得改所有调用点。工厂把“创建谁”这个决策收口,调用方只面向抽象(如 Logger*)写代码。
简单工厂:一个函数返回不同子类指针
不是 GoF 23 种之一,但最常用、最直接。核心是用参数控制返回哪个具体类型,返回类型统一为基类指针(或智能指针)。
常见错误:返回栈对象地址、忘记虚析构、字符串比较写错大小写。
- 基类必须有
virtual ~Base() = default;,否则通过基类指针 delete 派生对象会内存泄漏 - 工厂函数返回
std::unique_ptr<base>比裸指针更安全,避免手动delete - 参数建议用
const std::string&或枚举,避免传入非法字符串导致未定义行为(比如返回nullptr但调用方没检查)
class Logger {
public:
virtual ~Logger() = default;
virtual void log(const std::string& msg) = 0;
};
class FileLogger : public Logger {
public:
void log(const std::string& msg) override { /*...*/ }
};
std::unique_ptr<Logger> createLogger(const std::string& type) {
if (type == "file") return std::make_unique<FileLogger>();
if (type == "console") return std::make_unique<ConsoleLogger>();
return nullptr; // 调用方必须检查!
}
工厂方法:让子类决定实例化哪个类
适用于“创建逻辑本身也需扩展”的场景。比如每种数据库连接(MysqlConnection、PostgresConnection)对应一套初始化流程(加载驱动、设置超时、验证 SSL),这时把创建逻辑下放到子类的虚函数里更清晰。
立即学习“C++免费学习笔记(深入)”;
关键点:工厂基类定义 virtual std::unique_ptr<connection> createConnection() = 0;</connection>,每个子类重写它;调用方只依赖工厂基类,不关心具体工厂类型。
- 不要在基类工厂中 new 具体类型——那又回到简单工厂的老路
- 工厂子类通常不保存状态,只是封装创建逻辑,可设计为无状态单例或按需构造
- 如果创建过程需要传参(如连接串),参数应作为
createConnection()的参数,而不是存在工厂对象里
抽象工厂:创建一族相关/依赖的对象
当系统需要确保一组对象(如 UI 控件:按钮 + 文本框 + 下拉框)风格一致(Windows 风格 vs macOS 风格),且它们之间有协作关系(比如 MacButton 内部调用 MacTheme 渲染),这时单一工厂无法保证组合一致性,得用抽象工厂。
典型结构:一个抽象工厂接口定义多个创建函数(createButton()、createTextBox()),每个具体工厂实现整套创建逻辑。
- 抽象工厂本身不 new 任何东西,只声明接口;具体工厂才负责 new 和组装
- 如果某族新增一个产品(比如加个
Slider),所有具体工厂都得改——这是抽象工厂的扩展痛点,需权衡是否真需要“族内强一致性” - C++ 中常配合
std::shared_ptr返回,因为一族对象可能互相持有引用,生命周期需协同管理
容易被忽略的是:抽象工厂解决的不是“选哪个实现”,而是“选哪一整套实现并保证它们能一起工作”。一旦误当成简单工厂用(只调一个创建函数),就失去了设计意义。











