一、简单工厂模式的核心原理
1.1 模式定义与本质
简单工厂模式(Simple Factory Pattern)是一种创建型的设计模式,其核心思想是通过单一的工厂类根据传入的参数,动态决定创建哪种具体产品类的实例。该模式将对象的创建过程封装在工厂类中,使得客户端无需直接调用具体产品的构造函数,实际上是利用类的多态性,实现用子类的模型创建父类对象。
其本质是将对象创建与使用解耦,通过引入中间层(工厂类)来隔离变化点。当新增产品类型时,虽然仍需修改工厂类,但客户端代码无需变动,这在系统扩展初期具有显著优势。
1.2 模式结构解析
-
核心三要素:
抽象产品接口(AbstractProduct)
-
定义所有具体产品的公共操作接口
例如:class Operation { virtual double Calculate() = 0; }
具体产品实现(ConcreteProduct)
-
继承抽象接口实现具体业务逻辑
例如:加法类AddOperation、乘法类MultiplyOperation
工厂类(SimpleFactory)
-
包含静态/动态创建方法,根据参数生成对应产品实例
例如:OperationFactory::Create(char operator)
1.3 工作流程剖析
- 客户端调用工厂类的创建方法并传递类型参数;
- 工厂解析参数,通过条件判断实例化对应产品;
- 返回抽象产品接口指针,客户端通过接口调用方法;
- 销毁对象时通过抽象接口的虚析构函数保证资源释放;
模式对比:相较于工厂模式需要为每个产品建立子工厂,简单工厂模式通过集中判断逻辑降低了系统复杂度,但牺牲了扩展性。
二、典型应用场景分析
2.1 适用场景特征
- 产品类型有限,就是工厂生产的模型不多(通常不超过10种);
- 创建逻辑相对简单,无需复杂初始化过程;
- 系统扩展压力较小,产品类型变更频率低;
- 客户端不关心实例化细节,仅需使用统一接口;
2.2 行业经典案例
案例1:数学运算系统 :参数化对象生成
现实类比:就像快餐店的自动点餐机,按"汉堡"按钮出汉堡,按"薯条"按钮出薯条
cpp
// 抽象菜品接口
class IFood {
public:
virtual void prepare() = 0;
};
// 具体产品
class Hamburger : public IFood { /* 制作汉堡 */ };
class Fries : public IFood { /* 炸薯条 */ };
// 点餐机工厂
class FoodFactory {
public:
static IFood* createFood(const string& name) {
if(name == "汉堡") return new Hamburger();
if(name == "薯条") return new Fries();
throw "本店暂无此商品";
}
};
// 顾客点餐(客户端)
IFood* myOrder = FoodFactory::createFood("汉堡");
myOrder->prepare();
优势:将食物制作过程封装在后厨(工厂),顾客无需知道汉堡是怎么组装的。此实现完美体现了参数化对象创建的特性,客户端只需传入参数即可获得对应计算对象。
案例2:图形绘制工具
现实案例:Photoshop的图形创建工具栏
点击"圆形"工具生成圆形绘制器
点击"矩形"工具生成矩形绘制器
实现原理:
cpp
class Shape { /* 绘图接口 */ };
class Circle : public Shape { /* 画圆逻辑 */ };
class Rectangle : public Shape { /* 画矩形逻辑 */ };
// 工具面板工厂
Shape* createShape(ToolType type) {
switch(type) {
case CIRCLE: return new Circle();
case RECTANGLE: return new Rectangle();
}
}
案例3:游戏开发:武器生成系统
典型需求:根据玩家选择的武器类型(剑/弓/法杖)生成对应实例
工厂实现:
cpp
class Weapon {
public:
virtual void attack() = 0;
};
class Sword : public Weapon { /* 近战攻击逻辑 */ };
class Bow : public Weapon { /* 远程攻击逻辑 */ };
class WeaponShop {
public:
Weapon* purchaseWeapon(string type) {
if(type == "诛仙剑") return new Sword(1000);
if(type == "火焰弓") return new Bow(800);
throw "本店没有这种武器";
}
};
业务价值:游戏策划调整武器属性时,只需修改工厂创建逻辑,不影响战斗系统。
案例4:金融系统:支付渠道选择
业务场景:电商平台需要支持微信/支付宝/银联等多种支付方式
代码示例:
cpp
class Payment {
public:
virtual bool pay(double amount) = 0;
};
class WechatPay : public Payment { /* 微信支付 */ };
class Alipay : public Payment { /* 支付宝 */ };
class PaymentGateway {
public:
static Payment* createChannel(string method) {
if(method == "微信") return new WechatPay();
if(method == "支付宝") return new Alipay();
throw "不支持的支付方式";
}
};
调用方式:
cpp
Payment* payment = PaymentGateway::createChannel("支付宝");
payment->pay(19.9); // 完成支付
扩展成本:新增支付方式时,只需扩展工厂类和实现新支付类,不影响订单处理模块.
三、实际应用指南
3.1 设计实施步骤
- 识别变化维度:明确哪些产品类型需要动态创建;
- 抽象产品接口:提取所有产品的共性方法;
- 实现具体产品:每个产品独立实现业务逻辑;
- 构建工厂类:
使用switch-case或if-else分支结构;
推荐采用static方法实现无状态工厂; - 异常处理机制:
对非法参数抛出异常或返回空指针;
使用try-catch块保证资源安全;
3.2 参数化实现进阶
方案1:枚举类型参数
cpp
enum class ProductType { TYPE_A, TYPE_B, TYPE_C };
class Factory {
public:
static Product* create(ProductType type) {
// 根据枚举值创建对象
}
};
优势:编译期类型检查,避免字符串拼写错误。
方案2:注册表机制
cpp
class Factory {
using CreatorFunc = std::function<Product*()>;
std::unordered_map<std::string, CreatorFunc> registry;
public:
void registerProduct(const std::string& key, CreatorFunc func) {
registry[key] = func;
}
Product* create(const std::string& key) {
return registry.at(key)();
}
};
此方案通过运行时注册扩展产品类型,部分解决了违反开放封闭原则的问题。
四、常见问题与解决方案
4.1 违反开放封闭原则
问题表现:新增产品类型必须修改工厂类的判断逻辑
解决方案:
- 结合配置文件(XML/JSON)动态加载产品类型
- 使用宏定义自动注册产品创建器
- 采用模板元编程技术生成分支代码
4.2 工厂类职责过重
问题表现:随着产品数量增加,工厂方法变得臃肿
优化策略:
cpp
// 将创建逻辑拆分为多个工厂方法
class Factory {
static Product* createTypeA() { /*...*/ }
static Product* createTypeB() { /*...*/ }
public:
static Product* create(const std::string& type) {
if(type == "A") return createTypeA();
if(type == "B") return createTypeB();
}
};
4.3 类型扩展难题
典型场景:需要支持插件式架构时,简单工厂难以动态加载新类型
应对方案:
结合动态库(DLL/SO)实现热加载
使用抽象工厂模式进行二次封装
引入依赖注入框架(如Google Fruit)
五、模式演进与替代方案
当产品类型超过10种或创建逻辑变得复杂时,建议考虑以下演进方向:
5.1 升级为工厂方法模式
为每个产品创建独立工厂类。通过多态机制实现扩展。
缺点:类数量呈线性增长。
5.2 转型为抽象工厂模式
处理产品族创建场景。例如需要同时创建CPU、GPU等关联硬件。提供更高层次的抽象接口。
5.3 混合模式实践
cpp
// 简单工厂 + 单例模式
class Factory {
Factory() = default; // 私有构造函数
public:
static Factory& instance() {
static Factory factory;
return factory;
}
Product* create(/*...*/) { /*...*/ }
};
此实现保证了全局唯一的工厂实例,适用于资源密集型场景。
六、模式优劣辩证分析
6.1 核心优势
- 封装创建细节:客户端与具体实现解耦;
- 集中控制点:统一管理对象创建策略;
- 降低耦合度:产品实现变化不影响调用方;
- 快速实现原型:适用于敏捷开发初期阶段;
6.2 主要局限
- 违反OCP原则:扩展需修改源代码;
- 单一职责过载:工厂类成为系统瓶颈;
- 类型检查缺失:运行时错误风险增加;
- 难以应对复杂场景:嵌套创建、依赖注入等;
七、最佳实践总结
- 控制产品规模:建议将产品类型控制在5-8种以内;
- 防御式编程:对非法参数进行严格校验;
- 文档规范化:明确记录支持的参数类型;
- 性能优化:对频繁创建的对象采用对象池技术;
- 测试策略:
工厂方法的100%分支覆盖;
产品类的接口要进行契约测试;
跨产品组合的要进行集成测试;
简单工厂模式在一些中小型C++项目中的采用率很,但在大型框架中的使用率偏低,这也说明了与其设计定位的高度吻合。