目录
三、C++ 重构实战:用工厂方法模式打造 "可插拔" 支付系统
需求:支持解析 TXT、PDF、Word 文档,未来可能加 Markdown
需求:游戏支持战士、法师、牧师三种职业,每种职业有不同技能和属性

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
引言:为什么你总在 "new 对象" 时栽跟头?

作为 C++ 开发者,你一定写过这样的代码:
// 支付功能:根据类型创建不同支付对象
IPay* createPay(int type) {
if (type == 1) {
return new WechatPay(); // 硬编码微信支付
} else if (type == 2) {
return new Alipay(); // 硬编码支付宝
} else if (type == 3) {
return new UnionPay(); // 新增银联支付,又改这里
}
return nullptr;
}
这段代码看似简单,却藏着 3 个致命问题:
- 新增支付方式必改代码:产品要加 ApplePay?你得在
createPay里加else if (type == 4),违反开放封闭原则; - 业务逻辑与创建逻辑混杂:订单服务调用
createPay时,既要关心 "如何支付",还要知道 "怎么创建支付对象",职责混乱; - 测试成本高:改了
createPay后,所有依赖它的模块(订单、退款、对账)都得回归测试,生怕漏改if-else导致创建错误对象。
这些问题的根源,在于对象创建逻辑没有被妥善管理—— 直接用new硬编码具体类型,就像把 "零件生产" 和 "产品组装" 混在一个车间,效率低还容易出错。
而工厂方法模式(Factory Method Pattern),就是专门解决对象创建混乱的 "生产车间管理方案":通过定义 "抽象工厂" 和 "具体工厂",让对象创建逻辑与业务逻辑分离,新增类型时只需加新工厂,无需修改老代码。
这篇文章不会讲干巴巴的 UML 图,而是用 C++ 实战场景 + 可运行代码 + 真实踩坑经验,带你彻底吃透工厂方法模式。不管你是刚接触设计模式的新手,还是想摆脱 "new 对象泥潭" 的开发者,读完这篇,你都能写出 "创建逻辑灵活扩展,业务代码稳如泰山" 的优雅代码。
一、先搞懂:工厂方法模式到底是什么?

1.1 核心思想:"谁用谁创建" 不如 "专门工厂来创建"
工厂方法模式的核心是:定义一个创建对象的接口(抽象工厂),让子类(具体工厂)决定实例化哪个类(产品),将对象创建延迟到子类。
用一句大白话解释:别在业务代码里直接 new 对象了,交给专门的 "工厂" 去创建。比如支付系统中,微信支付由WechatPayFactory创建,支付宝由AlipayFactory创建,业务逻辑只需要调用工厂的 "创建方法",不用关心具体怎么 new。
举个生活化的例子:
- 传统做法(直接 new):你想吃披萨,自己动手做(自己 new),换个口味(新增类型)就得重新学做法;
- 工厂方法:你去披萨店(抽象工厂),点不同口味的披萨(具体产品),店员(具体工厂)帮你做,新增口味只需雇新店员,你不用学新做法。
这里的 "披萨店" 就是抽象工厂,"不同口味的店员" 是具体工厂,"披萨" 是产品 —— 业务逻辑(你)只和抽象工厂交互,具体创建交给子类。
1.2 解决什么问题?3 个核心痛点
工厂方法模式专门解决对象创建的 3 大痛点:
- 创建逻辑分散:到处都是
new和if-else,新增类型要改多个地方; - 业务与创建耦合:业务代码依赖具体产品类型,换产品就得改业务;
- 扩展困难:新增产品需要修改创建逻辑,违反开放封闭原则。
1.3 工厂方法的 4 个核心角色
理解工厂方法模式,只需记住 4 个角色:
- 抽象产品(Product):定义产品的接口(如
IPay),是所有具体产品的父类; - 具体产品(ConcreteProduct):实现抽象产品的具体类(如
WechatPay、Alipay); - 抽象工厂(Factory):定义创建产品的接口(如
IPayFactory),包含一个创建产品的纯虚方法; - 具体工厂(ConcreteFactory):实现抽象工厂的方法,负责创建具体产品(如
WechatPayFactory)。
它们的关系就像:抽象工厂→生产→抽象产品,具体工厂→生产→具体产品,业务逻辑只依赖 "抽象工厂" 和 "抽象产品",不碰具体实现。
1.4 和简单工厂的区别:别搞混了!
很多人分不清 "工厂方法" 和 "简单工厂",这里一句话说清:
- 简单工厂:一个工厂类负责所有产品的创建(用
switch判断),新增产品要改工厂类,违反开放封闭原则; - 工厂方法:每个具体产品对应一个具体工厂,新增产品只需加新工厂,不用改老代码,符合开放封闭原则。
简单说,简单工厂是 "一厂多品",工厂方法是 "一厂一品"—— 前者适合产品少且稳定的场景,后者适合产品多变的场景。
二、C++ 反例实战:没有工厂的 "支付系统" 有多坑?

为了让你直观感受工厂方法的价值,我们先实现一个 "没有工厂" 的支付系统,看看它在需求变更时的狼狈。
2.1 需求背景
实现一个电商支付系统,核心功能:
- 支持微信、支付宝两种支付方式,每种支付有
pay(发起支付)和refund(退款)方法; - 订单系统根据用户选择的支付方式,创建对应支付对象并调用支付方法;
- 未来可能新增银联、ApplePay 等支付方式。
2.2 反例代码:直接 new+switch 的混乱实现
#include <iostream>
#include <string>
// 抽象产品:支付接口
class IPay {
public:
virtual ~IPay() = default;
virtual void pay(double amount) = 0; // 发起支付
virtual void refund(double amount) = 0; // 退款
};
// 具体产品:微信支付
class WechatPay : public IPay {
public:
void pay(double amount) override {
std::cout << "微信支付:" << amount << "元" << std::endl;
}
void refund(double amount) override {
std::cout << "微信退款:" << amount << "元" << std::endl;
}
};
// 具体产品:支付宝
class Alipay : public IPay {
public:
void pay(double amount) override {
std::cout << "支付宝支付:" << amount << "元" << std::endl;
}
void refund(double amount) override {
std::cout << "支付宝退款:" << amount << "元" << std::endl;
}
};
// 业务模块:订单系统(直接创建支付对象,耦合具体类型)
class OrderSystem {
public:
// 根据类型创建支付对象(硬编码,违反开放封闭)
IPay* createPay(int payType) {
switch (payType) {
case 1: return new WechatPay(); // 直接new具体类型
case 2: return new Alipay(); // 直接new具体类型
default: return nullptr;
}
}
// 处理支付(业务逻辑与创建逻辑混杂)
void processPay(int payType, double amount) {
IPay* pay = createPay(payType);
if (pay) {
pay->pay(amount);
delete pay; // 手动释放,容易漏
}
}
};
// 主函数测试
int main() {
OrderSystem orderSystem;
orderSystem.processPay(1, 199.9); // 微信支付
orderSystem.processPay(2, 299.9); // 支付宝支付
return 0;
}
2.3 反例代码的 3 大致命问题
这段代码能跑,但当产品要求 "新增银联支付" 时,问题集中爆发:
- 必须修改业务代码:要在
OrderSystem的createPay里加case 3: return new UnionPay(),违反 "对修改关闭" 原则,改完还得重新测试整个订单系统; - 创建逻辑与业务耦合:
OrderSystem既要管订单业务,又要负责支付对象创建,违背单一职责原则,代码越来越臃肿; - 扩展成本高:如果后续还要加 "支付方式配置中心"(比如从数据库读支付类型),
createPay里的switch会越来越长,变成 "if-else 地狱"。
更隐蔽的问题是:如果WechatPay的构造函数需要新增参数(比如appId),所有new WechatPay()的地方都得改 —— 这就是直接new具体类型的 "牵一发而动全身"。
三、C++ 重构实战:用工厂方法模式打造 "可插拔" 支付系统

针对反例的问题,我们用工厂方法模式重构,核心思路是 **"创建逻辑抽离到工厂,业务只认抽象工厂"**:
- 定义抽象工厂
IPayFactory,包含创建支付对象的纯虚方法; - 为每个具体支付方式创建对应工厂(
WechatPayFactory、AlipayFactory); - 业务模块
OrderSystem只依赖IPayFactory,通过工厂获取支付对象,不碰具体类型。
3.1 重构后的完整代码
3.1.1 抽象产品与具体产品(支付方式)
// IPay.h(抽象产品)
#ifndef I_PAY_H
#define I_PAY_H
#include <string>
// 支付接口:所有支付方式的抽象
class IPay {
public:
virtual ~IPay() = default;
virtual void pay(double amount) = 0; // 发起支付
virtual void refund(double amount) = 0; // 退款
virtual std::string getPayName() const = 0; // 获取支付名称
};
#endif // I_PAY_H
// WechatPay.h(具体产品:微信支付)
#ifndef WECHAT_PAY_H
#define WECHAT_PAY_H
#include "IPay.h"
class WechatPay : public IPay {
public:
void pay(double amount) override;
void refund(double amount) override;
std::string getPayName() const override;
};
#endif // WECHAT_PAY_H
// WechatPay.cpp
#include "WechatPay.h"
#include <iostream>
void WechatPay::pay(double amount) {
std::cout << "[微信支付] 发起支付:" << amount << "元" << std::endl;
// 实际调用微信支付SDK的逻辑(简化)
}
void WechatPay::refund(double amount) {
std::cout << "[微信支付] 发起退款:" << amount << "元" << std::endl;
}
std::string WechatPay::getPayName() const {
return "微信支付";
}
// Alipay.h(具体产品:支付宝)
#ifndef ALIPAY_H
#define ALIPAY_H
#include "IPay.h"
class Alipay : public IPay {
public:
void pay(double amount) override;
void refund(double amount) override;
std::string getPayName() const override;
};
#endif // ALIPAY_H
// Alipay.cpp
#include "Alipay.h"
#include <iostream>
void Alipay::pay(double amount) {
std::cout << "[支付宝] 发起支付:" << amount << "元" << std::endl;
// 实际调用支付宝SDK的逻辑(简化)
}
void Alipay::refund(double amount) {
std::cout << "[支付宝] 发起退款:" << amount << "元" << std::endl;
}
std::string Alipay::getPayName() const {
return "支付宝";
}
3.1.2 抽象工厂与具体工厂(创建支付对象)
// IPayFactory.h(抽象工厂)
#ifndef I_PAY_FACTORY_H
#define I_PAY_FACTORY_H
#include "IPay.h"
#include <memory> // 用智能指针管理对象生命周期
// 支付工厂接口:定义创建支付对象的契约
class IPayFactory {
public:
virtual ~IPayFactory() = default;
// 纯虚方法:创建支付对象(返回智能指针,避免内存泄漏)
virtual std::unique_ptr<IPay> createPay() = 0;
};
#endif // I_PAY_FACTORY_H
// WechatPayFactory.h(具体工厂:创建微信支付)
#ifndef WECHAT_PAY_FACTORY_H
#define WECHAT_PAY_FACTORY_H
#include "IPayFactory.h"
#include "WechatPay.h"
class WechatPayFactory : public IPayFactory {
public:
// 实现创建方法:返回微信支付对象
std::unique_ptr<IPay> createPay() override {
// 这里可以添加微信支付的初始化逻辑(如配置appId、密钥)
return std::make_unique<WechatPay>();
}
};
#endif // WECHAT_PAY_FACTORY_H
// AlipayFactory.h(具体工厂:创建支付宝)
#ifndef ALIPAY_FACTORY_H
#define ALIPAY_FACTORY_H
#include "IPayFactory.h"
#include "Alipay.h"
class AlipayFactory : public IPayFactory {
public:
// 实现创建方法:返回支付宝对象
std::unique_ptr<IPay> createPay() override {
// 这里可以添加支付宝的初始化逻辑(如配置appId、私钥)
return std::make_unique<Alipay>();
}
};
#endif // ALIPAY_FACTORY_H
3.1.3 业务模块:订单系统(只依赖抽象)
// OrderSystem.h(业务模块)
#ifndef ORDER_SYSTEM_H
#define ORDER_SYSTEM_H
#include "IPayFactory.h"
#include <string>
// 订单系统:只依赖抽象工厂和抽象产品,不碰具体实现
class OrderSystem {
public:
// 接收抽象工厂作为参数,不关心具体是哪个工厂
void processPay(IPayFactory& factory, double amount) {
// 通过工厂创建支付对象(多态)
std::unique_ptr<IPay> pay = factory.createPay();
if (pay) {
std::cout << "订单支付开始,支付方式:" << pay->getPayName() << std::endl;
pay->pay(amount); // 调用支付方法(多态)
}
}
void processRefund(IPayFactory& factory, double amount) {
std::unique_ptr<IPay> pay = factory.createPay();
if (pay) {
std::cout << "订单退款开始,支付方式:" << pay->getPayName() << std::endl;
pay->refund(amount); // 调用退款方法(多态)
}
}
};
#endif // ORDER_SYSTEM_H
3.1.4 新增银联支付(无需修改老代码)
// UnionPay.h(新增具体产品:银联支付)
#ifndef UNION_PAY_H
#define UNION_PAY_H
#include "IPay.h"
class UnionPay : public IPay {
public:
void pay(double amount) override;
void refund(double amount) override;
std::string getPayName() const override;
};
#endif // UNION_PAY_H
// UnionPay.cpp
#include "UnionPay.h"
#include <iostream>
void UnionPay::pay(double amount) {
std::cout << "[银联支付] 发起支付:" << amount << "元" << std::endl;
}
void UnionPay::refund(double amount) {
std::cout << "[银联支付] 发起退款:" << amount << "元" << std::endl;
}
std::string UnionPay::getPayName() const {
return "银联支付";
}
// UnionPayFactory.h(新增具体工厂:创建银联支付)
#ifndef UNION_PAY_FACTORY_H
#define UNION_PAY_FACTORY_H
#include "IPayFactory.h"
#include "UnionPay.h"
class UnionPayFactory : public IPayFactory {
public:
std::unique_ptr<IPay> createPay() override {
// 银联支付的初始化逻辑(如配置商户号)
return std::make_unique<UnionPay>();
}
};
#endif // UNION_PAY_FACTORY_H
3.1.5 主函数:测试工厂方法的灵活性
// main.cpp
#include <iostream>
#include "OrderSystem.h"
#include "WechatPayFactory.h"
#include "AlipayFactory.h"
#include "UnionPayFactory.h"
int main() {
OrderSystem orderSystem;
// 1. 微信支付
std::cout << "=== 测试微信支付 ===" << std::endl;
WechatPayFactory wechatFactory;
orderSystem.processPay(wechatFactory, 199.9); // 传入微信工厂
orderSystem.processRefund(wechatFactory, 199.9);
// 2. 支付宝支付
std::cout << "\n=== 测试支付宝支付 ===" << std::endl;
AlipayFactory alipayFactory;
orderSystem.processPay(alipayFactory, 299.9); // 传入支付宝工厂
orderSystem.processRefund(alipayFactory, 299.9);
// 3. 新增银联支付(无需修改OrderSystem)
std::cout << "\n=== 测试银联支付 ===" << std::endl;
UnionPayFactory unionFactory;
orderSystem.processPay(unionFactory, 399.9); // 传入银联工厂
orderSystem.processRefund(unionFactory, 399.9);
return 0;
}
3.2 编译与运行说明
3.2.1 编译命令(GCC)
g++ -std=c++11 main.cpp WechatPay.cpp Alipay.cpp UnionPay.cpp -o pay_system
- 依赖:C++11 及以上标准(支持
std::unique_ptr和override); - 编译环境:Linux/GCC、Windows/MinGW、Mac/Clang 均可。
3.2.2 运行结果
=== 测试微信支付 ===
订单支付开始,支付方式:微信支付
[微信支付] 发起支付:199.9元
订单退款开始,支付方式:微信支付
[微信支付] 发起退款:199.9元
=== 测试支付宝支付 ===
订单支付开始,支付方式:支付宝
[支付宝] 发起支付:299.9元
订单退款开始,支付方式:支付宝
[支付宝] 发起退款:299.9元
=== 测试银联支付 ===
订单支付开始,支付方式:银联支付
[银联支付] 发起支付:399.9元
订单退款开始,支付方式:银联支付
[银联支付] 发起退款:399.9元
3.3 重构后的核心优势(对比反例)
重构后的代码完全符合工厂方法模式,解决了反例的所有问题:
- 新增支付方式无需修改老代码:加银联支付时,只需新增
UnionPay(具体产品)和UnionPayFactory(具体工厂),OrderSystem和原有工厂代码一行不动,符合开放封闭原则; - 创建逻辑与业务彻底分离:
OrderSystem只负责订单业务,支付对象的创建交给工厂,职责清晰,代码可读性大幅提升; - 灵活切换支付方式:业务模块通过 "传入不同工厂" 实现支付方式切换,比如从微信换成支付宝,只需改一行代码(换工厂实例);
- 避免内存泄漏:用
std::unique_ptr管理支付对象生命周期,无需手动delete,比反例的原始指针更安全; - 初始化逻辑集中管理:每个工厂的
createPay方法可以封装产品的初始化细节(如配置密钥、连接 SDK),避免创建逻辑散落在业务代码中。
四、扩展实战:工厂方法模式的 3 个经典应用场景

工厂方法模式的应用远不止支付系统,在 C++ 开发中,凡是需要 "灵活创建对象" 的场景,都能用上。下面通过 3 个真实场景,展示模式的灵活性。
4.1 场景 1:日志系统(多输出目标)

需求:支持控制台日志、文件日志、数据库日志,可动态切换
工厂方法实现:
// 1. 抽象产品:日志接口
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& msg) = 0;
};
// 2. 具体产品:控制台日志
class ConsoleLogger : public ILogger {
public:
void log(const std::string& msg) override {
std::cout << "[控制台] " << msg << std::endl;
}
};
// 3. 具体产品:文件日志
class FileLogger : public ILogger {
private:
std::string filePath_;
public:
explicit FileLogger(std::string path) : filePath_(std::move(path)) {}
void log(const std::string& msg) override {
// 实际写入文件的逻辑(简化)
std::cout << "[文件][" << filePath_ << "] " << msg << std::endl;
}
};
// 4. 抽象工厂:日志工厂
class ILoggerFactory {
public:
virtual ~ILoggerFactory() = default;
virtual std::unique_ptr<ILogger> createLogger() = 0;
};
// 5. 具体工厂:控制台日志工厂
class ConsoleLoggerFactory : public ILoggerFactory {
public:
std::unique_ptr<ILogger> createLogger() override {
return std::make_unique<ConsoleLogger>();
}
};
// 6. 具体工厂:文件日志工厂(带参数)
class FileLoggerFactory : public ILoggerFactory {
private:
std::string filePath_;
public:
explicit FileLoggerFactory(std::string path) : filePath_(std::move(path)) {}
std::unique_ptr<ILogger> createLogger() override {
// 传递参数给具体产品
return std::make_unique<FileLogger>(filePath_);
}
};
// 7. 业务模块:日志服务
class LogService {
public:
// 依赖抽象工厂
void writeLog(ILoggerFactory& factory, const std::string& msg) {
auto logger = factory.createLogger();
logger->log(msg);
}
};
// 测试代码
int main() {
LogService logService;
// 控制台日志
ConsoleLoggerFactory consoleFactory;
logService.writeLog(consoleFactory, "用户登录成功");
// 文件日志(带路径参数)
FileLoggerFactory fileFactory("app.log");
logService.writeLog(fileFactory, "订单创建成功");
return 0;
}
运行结果:
[控制台] 用户登录成功
[文件][app.log] 订单创建成功
优势:新增 "数据库日志" 时,只需加DatabaseLogger和DatabaseLoggerFactory,LogService完全不用改,甚至可以在运行时根据配置文件选择日志工厂。
4.2 场景 2:文档解析器(多格式支持)

需求:支持解析 TXT、PDF、Word 文档,未来可能加 Markdown
工厂方法实现:
// 1. 抽象产品:文档解析器
class IDocumentParser {
public:
virtual ~IDocumentParser() = default;
virtual void parse(const std::string& filePath) = 0; // 解析文档
virtual std::string getFormat() const = 0; // 获取格式名称
};
// 2. 具体产品:TXT解析器
class TxtParser : public IDocumentParser {
public:
void parse(const std::string& filePath) override {
std::cout << "解析TXT文件:" << filePath << std::endl;
}
std::string getFormat() const override { return "TXT"; }
};
// 3. 具体产品:PDF解析器
class PdfParser : public IDocumentParser {
public:
void parse(const std::string& filePath) override {
std::cout << "解析PDF文件:" << filePath << std::endl;
}
std::string getFormat() const override { return "PDF"; }
};
// 4. 抽象工厂:解析器工厂
class IDocumentParserFactory {
public:
virtual ~IDocumentParserFactory() = default;
virtual std::unique_ptr<IDocumentParser> createParser() = 0;
};
// 5. 具体工厂:TXT解析器工厂
class TxtParserFactory : public IDocumentParserFactory {
public:
std::unique_ptr<IDocumentParser> createParser() override {
return std::make_unique<TxtParser>();
}
};
// 6. 具体工厂:PDF解析器工厂
class PdfParserFactory : public IDocumentParserFactory {
public:
std::unique_ptr<IDocumentParser> createParser() override {
return std::make_unique<PdfParser>();
}
};
// 7. 业务模块:文档处理服务
class DocumentService {
public:
void processDocument(IDocumentParserFactory& factory, const std::string& filePath) {
auto parser = factory.createParser();
std::cout << "开始处理" << parser->getFormat() << "文档..." << std::endl;
parser->parse(filePath);
}
};
// 测试代码
int main() {
DocumentService docService;
// 解析TXT
TxtParserFactory txtFactory;
docService.processDocument(txtFactory, "readme.txt");
// 解析PDF
PdfParserFactory pdfFactory;
docService.processDocument(pdfFactory, "report.pdf");
return 0;
}
运行结果:
开始处理TXT文档...
解析TXT文件:readme.txt
开始处理PDF文档...
解析PDF文件:report.pdf
优势:每个解析器的创建逻辑封装在对应工厂,业务模块不用关心 "PDF 解析需要依赖哪个库",只需调用工厂即可,符合依赖倒置原则。
4.3 场景 3:游戏角色创建(多职业系统)

需求:游戏支持战士、法师、牧师三种职业,每种职业有不同技能和属性
工厂方法实现:
// 1. 抽象产品:游戏角色
class ICharacter {
public:
virtual ~ICharacter() = default;
virtual void attack() = 0; // 攻击技能
virtual void defend() = 0; // 防御技能
virtual std::string get职业() const = 0;
};
// 2. 具体产品:战士
class Warrior : public ICharacter {
public:
void attack() override { std::cout << "战士使用劈砍!" << std::endl; }
void defend() override { std::cout << "战士举起盾牌防御!" << std::endl; }
std::string get职业() const override { return "战士"; }
};
// 3. 具体产品:法师
class Mage : public ICharacter {
public:
void attack() override { std::cout << "法师释放火球术!" << std::endl; }
void defend() override { std::cout << "法师开启魔法护盾!" << std::endl; }
std::string get职业() const override { return "法师"; }
};
// 4. 抽象工厂:角色工厂
class ICharacterFactory {
public:
virtual ~ICharacterFactory() = default;
virtual std::unique_ptr<ICharacter> createCharacter() = 0;
};
// 5. 具体工厂:战士工厂
class WarriorFactory : public ICharacterFactory {
public:
std::unique_ptr<ICharacter> createCharacter() override {
return std::make_unique<Warrior>();
}
};
// 6. 具体工厂:法师工厂
class MageFactory : public ICharacterFactory {
public:
std::unique_ptr<ICharacter> createCharacter() override {
return std::make_unique<Mage>();
}
};
// 7. 业务模块:游戏系统
class GameSystem {
public:
void createAndPlay(ICharacterFactory& factory) {
auto character = factory.createCharacter();
std::cout << "创建" << character->get职业() << "成功!" << std::endl;
character->attack();
character->defend();
}
};
// 测试代码
int main() {
GameSystem game;
// 创建战士
WarriorFactory warriorFactory;
game.createAndPlay(warriorFactory);
// 创建法师
MageFactory mageFactory;
game.createAndPlay(mageFactory);
return 0;
}
运行结果:
创建战士成功!
战士使用劈砍!
战士举起盾牌防御!
创建法师成功!
法师释放火球术!
法师开启魔法护盾!
优势:新增 "牧师" 职业时,只需加Priest和PriestFactory,游戏核心逻辑GameSystem完全不变,甚至可以在游戏运行时让玩家选择工厂创建不同角色。
五、C++ 实战避坑指南:工厂方法模式的 5 个常见误区

工厂方法模式看似简单,但在实际应用中,很多开发者会因理解偏差写出 "反模式" 代码。下面列出 5 个典型误区及避坑方案。
误区 1:工厂接口设计过粗,一个工厂创建多种产品
比如定义IAllFactory,包含createPay()、createLogger()、createParser()等方法,让一个工厂负责所有产品的创建 —— 这违背了 "单一职责原则",工厂会变得臃肿,新增产品还得改工厂接口。
避坑方案:一个抽象工厂只负责一类产品的创建(如IPayFactory只创建支付相关产品),不同类产品对应不同工厂,避免 "万能工厂"。
误区 2:工厂方法返回具体产品类型,而非抽象产品
比如WechatPayFactory的createPay()返回WechatPay*而非IPay*,导致业务模块依赖具体产品,失去多态优势。
避坑方案:工厂方法的返回值必须是抽象产品类型(如std::unique_ptr<IPay>),确保业务模块只认抽象,不认具体。
误区 3:滥用工厂模式,简单对象也用工厂创建
比如创建User、Order这类简单 POJO 对象(只有属性和 getter/setter),也非要整个UserFactory—— 这会增加代码复杂度,完全没必要。
避坑方案:工厂方法适合创建 "复杂对象"(如需要初始化配置、依赖外部资源、有复杂构造逻辑的对象),简单对象直接new即可。
误区 4:具体工厂不对外暴露,在业务中硬编码工厂创建
比如业务代码里写IPayFactory* factory = new WechatPayFactory(),虽然避免了直接new产品,但又硬编码了工厂类型,换工厂还得改业务代码。
避坑方案:用 "工厂的工厂"(抽象工厂的工厂)或配置文件动态创建工厂,比如:
// 工厂的工厂:根据类型字符串创建具体工厂
std::unique_ptr<IPayFactory> createPayFactory(const std::string& type) {
if (type == "wechat") {
return std::make_unique<WechatPayFactory>();
} else if (type == "alipay") {
return std::make_unique<AlipayFactory>();
}
return nullptr;
}
// 业务中通过配置字符串获取工厂,避免硬编码
std::string payType = getConfig("pay_type"); // 从配置文件读
auto factory = createPayFactory(payType);
误区 5:忽视 C++ 特性,工厂方法参数传递不当
比如具体工厂需要参数(如文件日志的路径),却在工厂构造函数中硬编码,导致工厂灵活性不足。
避坑方案:通过工厂的构造函数传递参数,让工厂在创建时配置初始化信息,如:
// 文件日志工厂:通过构造函数接收路径参数
class FileLoggerFactory : public ILoggerFactory {
private:
std::string path_;
public:
explicit FileLoggerFactory(std::string path) : path_(std::move(path)) {}
std::unique_ptr<ILogger> createLogger() override {
return std::make_unique<FileLogger>(path_); // 传递参数给产品
}
};
六、总结:工厂方法模式的核心与实战建议

6.1 核心要点提炼
- 核心思想:将对象创建延迟到具体工厂,业务逻辑只依赖抽象工厂和抽象产品;
- 4 个角色:抽象产品(Product)、具体产品(ConcreteProduct)、抽象工厂(Factory)、具体工厂(ConcreteFactory);
- 核心价值:解耦对象创建与业务逻辑,支持灵活扩展,符合开放封闭原则;
- 适用场景:产品类型多变、创建逻辑复杂、需要统一管理对象创建的场景。
6.2 C++ 实战建议
-
抽象设计:
- 抽象产品用纯虚类定义,包含必要的业务方法;
- 抽象工厂只定义一个
create方法(单一职责),返回抽象产品的智能指针; - 优先用
std::unique_ptr管理产品生命周期,避免内存泄漏。
-
具体实现:
- 具体工厂的
create方法负责产品的完整创建(包括初始化、配置); - 产品需要参数时,通过工厂的构造函数传递,再由工厂传递给产品;
- 新增产品时,同步新增对应的具体工厂,确保 "一厂一品"。
- 具体工厂的
-
业务调用:
- 业务模块通过 "注入工厂" 而非 "直接创建工厂" 来获取产品;
- 避免在业务中硬编码具体工厂类型,可通过配置或工厂的工厂动态获取。
-
与其他模式结合:
- 结合依赖注入:在大型项目中,用依赖注入框架管理工厂的创建和注入;
- 结合原型模式:如果产品创建成本高,工厂可以先创建原型,再通过克隆生成产品;
- 结合单例模式:如果工厂无状态,可将具体工厂设计为单例,避免重复创建。
6.3 最后一句话
工厂方法模式的本质,是 "用抽象隔离对象创建的变化"。它不会让你的代码行数减少,但会让代码的 "变化部分" 和 "稳定部分" 泾渭分明 —— 当新增产品时,你只需关注新工厂和新产品的实现,不用再在老代码中 "缝缝补补"。
记住:好的代码不是 "一次写对",而是 "在需求变更时,能以最小代价修改"。工厂方法模式,就是帮你实现这一点的利器。

被折叠的 条评论
为什么被折叠?



