简单工厂模式 vs 策略模式:UML看着像,核心职责、使用场景、目的完全不同
一、先纠正你的直观错觉:UML结构只是类组成相似,但角色分工、依赖方向、解决问题天差地别
先统一两套模式标准UML角色,再对比差异。
1. 两套模式标准UML角色拆解
(1)简单工厂模式(静态工厂)
角色构成:
- 抽象产品 Product:所有产品统一抽象接口(比如
Operation加减乘除运算) - 具体产品 ConcreteProductA/B/C:实现抽象产品的具体类(Add、Sub、Mul)
- 工厂 Factory(核心):唯一创建者,提供静态方法
createProduct(type),根据参数判断,new 对应具体产品实例,返回抽象产品。 - 客户端 Client:只依赖「工厂」+「抽象产品」,不直接new具体产品;客户端把创建参数传给工厂,由工厂全权负责实例创建。
UML关系:
- Factory → 依赖 Product;Factory 实例化 ConcreteProduct
- Client → 依赖 Factory + Product
核心动作:工厂负责「对象创建」。
(2)策略模式
角色构成:
- 抽象策略 Strategy:统一算法接口(比如
PayStrategy支付:微信/支付宝/银行卡) - 具体策略 ConcreteStrategyA/B/C:实现抽象策略,写不同算法逻辑(WechatPay、AliPay)
- 上下文 Context(核心):持有一个
Strategy成员变量,对外提供业务方法execute(),内部调用策略的算法;提供setStrategy()动态切换策略。 - 客户端 Client:自己new具体策略实例,传给Context;客户端自主选择要使用的策略。
UML关系:
- Context 聚合 Strategy(持有引用)
- Client → 依赖 Context + Strategy
核心动作:上下文负责「使用算法/执行业务」,策略只封装可变算法。
二、最核心4点本质区别(重点)
区别1:设计目标、解决的问题完全不同
简单工厂:解决「对象创建复杂」的问题
痛点:产品种类多、创建逻辑复杂、创建参数耦合在业务代码里,不想让客户端到处
new XXX()。
核心职责:封装实例化过程,统一对象创建入口。
- 关注点:怎么造对象
- 变化点:新增产品时,只改工厂的if/switch判断分支(违反开闭原则,这也是简单工厂不属于GOF23设计模式的原因)
策略模式:解决「算法/业务逻辑频繁变化、大量if-else业务分支」的问题
痛点:同一个业务流程,多种可替换算法;大量if-else嵌套在业务类中,难以维护、扩展。
核心职责:把可变算法抽离独立类,实现算法与业务解耦,动态切换。
- 关注点:怎么使用对象、运行时切换行为
- 变化点:新增算法只需要新增ConcreteStrategy,完全不用改Context,符合开闭原则
区别2:核心主控角色完全不一样
- 简单工厂核心 = 工厂类 Factory
所有实例创建逻辑全部收拢在工厂,工厂是整个流程的控制中心;客户端只告诉工厂“我要什么类型”,创建过程完全交给工厂,客户端不参与new具体类。
示例:客户端传type="add",工厂内部new Add()返回给客户端。 - 策略模式核心 = 上下文 Context
Context是业务载体,负责串联固定业务流程;策略只是一段可替换的逻辑。客户端自己创建具体策略,注入Context;Context只负责调用策略,不负责创建策略。
示例:客户端ctx = new OrderContext(); ctx.setStrategy(new WechatPay()); ctx.pay();
区别3:客户端权责、实例创建权归属不同
简单工厂:客户端不创建具体对象,工厂全权创建
// 简单工厂客户端代码
Operation op = OperationFactory.createOperation("+"); // 客户端只传标识,不new Add
int res = op.calc(1,2);
- 客户端只持有抽象产品,完全不感知具体产品类;
- 实例创建逻辑被屏蔽在工厂内部。
策略模式:客户端必须手动创建具体策略,注入上下文
// 策略模式客户端代码
PayStrategy pay = new WechatPay(); // 客户端自己new具体策略
OrderContext ctx = new OrderContext(pay);
ctx.doPay(99);
// 运行时动态切换
ctx.setStrategy(new AliPay());
- 客户端明确知道自己要用哪一个具体策略;
- Context不会主动创建策略,只负责持有、调用策略。
区别4:开闭原则支持度、扩展代价不同
-
简单工厂:不满足开闭原则
新增一个产品,必须修改工厂里的分支判断(if/switch),修改原有代码,会引入bug风险。
例:新增除法Div,要去OperationFactory加case "/" : return new Div()。 -
策略模式:完美满足开闭原则
新增算法,只新增一个ConcreteStrategy实现类,Context、原有策略完全不用修改,无侵入。
例:新增数字人民币支付,新建DigitalRMBPay实现PayStrategy,上下文一行不用改。
三、举同场景对比示例,直观感受差异
场景:计算器,支持加减乘除四种运算
方案A:用简单工厂实现
- 抽象产品:
Operation提供calc(int a,int b) - 具体产品:Add、Sub、Mul、Div
- 工厂:
OperationFactory,静态方法根据符号返回运算实例 - 客户端:只传符号,由工厂创建运算对象,执行计算
重心:怎么把加减乘除的new操作统一管理起来,避免业务代码到处new。
方案B:用策略模式实现
- 抽象策略:
CalcStrategy提供calc(int a,int b) - 具体策略:AddStrategy、SubStrategy…
- 上下文:
CalcContext,持有CalcStrategy,提供execute()执行计算 - 客户端:手动new AddStrategy,传给CalcContext,调用execute;随时替换策略
重心:计算是固定业务流程,加减乘除是可变算法,做到运行时切换算法、解耦业务和计算逻辑。
四、为什么UML图看起来几乎一样?
表层相似点只有2个:
- 都存在「抽象父接口 + 多个具体实现类」的分层;
- 都有一个中间类(工厂/上下文)依赖顶层抽象接口。
但底层本质完全相反:
- 中间类的职责不同:工厂造对象,上下文用对象;
- 实例创建主体不同:工厂内部new vs 客户端外部new;
- 设计目的不同:封装创建逻辑 vs 封装可变算法;
- 扩展特性不同:违反开闭 vs 遵循开闭。
五、快速选择判断口诀
- 如果你想统一管理对象的创建过程,屏蔽复杂new逻辑 → 简单工厂;
- 如果你有同一套业务流程、多种可替换算法,想消除大量if-else、运行时切换逻辑 → 策略模式;
- 如果你既要动态切换算法,又不想客户端手动new策略 → 可以策略+简单工厂组合使用(工厂负责创建策略,注入Context)。
六、补充:组合使用案例(解决策略模式客户端要new的痛点)
把简单工厂嵌入策略模式,工厂负责创建策略,客户端只传标识,既拥有开闭特性,又屏蔽创建逻辑:
// 工厂创建策略
PayStrategy strategy = StrategyFactory.getStrategy("wechat");
OrderContext ctx = new OrderContext(strategy);
ctx.doPay();
此时:
- 策略模式负责解耦业务与支付算法;
- 简单工厂负责屏蔽策略实例的创建细节;
二者各司其职,互不冲突。
C++ 策略模式完整实战示例
业务场景
商场收银系统,3种优惠算法可动态切换:
- 正常原价收费
- 打折(如8折)
- 满减(满300减100)
同一套收银流程,优惠算法可运行时替换,完美契合策略模式。
模式角色对应
- 抽象策略 Strategy:
CashStrategy统一收费算法接口 - 具体策略 ConcreteStrategy:正常收费/打折/满减,各自实现计费逻辑
- 上下文 Context:
CashContext,持有策略对象,对外提供统一收银接口,负责执行算法 - 客户端 Client:选择策略、传入上下文、执行计费
#include <iostream>
#include <memory>
// ===================== 1. 抽象策略类:收费算法接口 =====================
class CashStrategy
{
public:
virtual ~CashStrategy() = default;
// 接收原价,返回实际收款金额
virtual double acceptCash(double money) const = 0;
};
// ===================== 2. 具体策略实现类 =====================
// 策略1:正常收费,无优惠
class CashNormal : public CashStrategy
{
public:
double acceptCash(double money) const override
{
return money;
}
};
// 策略2:打折收费,构造传入折扣(0.8=8折)
class CashRebate : public CashStrategy
{
private:
double rebate;
public:
explicit CashRebate(double r) : rebate(r) {}
double acceptCash(double money) const override
{
return money * rebate;
}
};
// 策略3:满减收费,满condition减reduce
class CashReturn : public CashStrategy
{
private:
double condition; // 满多少
double reduce; // 减多少
public:
CashReturn(double cond, double red) : condition(cond), reduce(red) {}
double acceptCash(double money) const override
{
if (money >= condition)
{
// 支持多次满减,如满300减100,600减200
double times = money / condition;
return money - times * reduce;
}
return money;
}
};
// ===================== 3. 上下文 Context:收银上下文 =====================
// 核心:持有策略对象,封装固定收银流程,对外屏蔽算法细节
class CashContext
{
private:
std::unique_ptr<CashStrategy> strategy; // 持有策略(聚合关系)
public:
// 构造注入策略
explicit CashContext(std::unique_ptr<CashStrategy> s) : strategy(std::move(s)) {}
// 运行时动态切换策略
void setStrategy(std::unique_ptr<CashStrategy> s)
{
strategy = std::move(s);
}
// 统一对外收银接口,内部调用策略算法
double getResult(double money) const
{
return strategy->acceptCash(money);
}
};
// ===================== 4. 客户端测试代码 =====================
int main()
{
double originMoney = 1000; // 商品原价1000元
std::cout << "===== 1. 使用原价策略 =====" << std::endl;
CashContext ctx1(std::make_unique<CashNormal>());
std::cout << "应付金额:" << ctx1.getResult(originMoney) << std::endl;
std::cout << "\n===== 2. 使用8折优惠策略 =====" << std::endl;
CashContext ctx2(std::make_unique<CashRebate>(0.8));
std::cout << "应付金额:" << ctx2.getResult(originMoney) << std::endl;
std::cout << "\n===== 3. 使用满300减100策略 =====" << std::endl;
CashContext ctx3(std::make_unique<CashReturn>(300, 100));
std::cout << "应付金额:" << ctx3.getResult(originMoney) << std::endl;
std::cout << "\n===== 4. 运行时动态切换策略:原价 → 5折 =====" << std::endl;
CashContext ctx4(std::make_unique<CashNormal>());
std::cout << "切换前:" << ctx4.getResult(originMoney) << std::endl;
// 动态替换算法,无需修改上下文代码
ctx4.setStrategy(std::make_unique<CashRebate>(0.5));
std::cout << "切换后:" << ctx4.getResult(originMoney) << std::endl;
return 0;
}
输出结果
===== 1. 使用原价策略 =====
应付金额:1000
===== 2. 使用8折优惠策略 =====
应付金额:800
===== 3. 使用满300减100策略 =====
应付金额:700
===== 4. 运行时动态切换策略:原价 → 5折 =====
切换前:1000
切换后:500
核心特性解读(对应策略模式本质)
- 算法与业务解耦
计费优惠逻辑全部抽离为独立策略类,收银上下文CashContext只关心“收钱”这个固定业务,不关心优惠怎么算。 - 运行时动态切换
setStrategy()可以在程序运行中替换算法,不需要新建上下文对象。 - 符合开闭原则
新增优惠活动(比如满赠、积分抵扣),只需要新建CashStrategy派生类,不用修改Context和原有策略代码。 - UML关系说明
CashContext聚合CashStrategy(持有指针/智能指针)- 多个具体类继承抽象策略
- 客户端创建具体策略,注入上下文,上下文不负责创建策略(和简单工厂最核心区别)
和简单工厂的关键区分(结合这段代码)
- 策略模式:客户端自己
make_unique<CashRebate>(0.8)创建策略,上下文只使用、不生产对象; - 简单工厂:会单独写
CashFactory,客户端只传字符串"rebate",工厂内部new CashRebate,屏蔽创建细节; - 组合使用方案:可以新增工厂类,封装策略的创建,解决客户端手动new策略繁琐的问题。
拓展:策略+简单工厂 混合改造(简化客户端)
新增工厂,客户端不用手动new各种策略,只传类型标识:
class CashStrategyFactory
{
public:
static std::unique_ptr<CashStrategy> createStrategy(const std::string& type)
{
if (type == "normal")
return std::make_unique<CashNormal>();
else if (type == "rebate8")
return std::make_unique<CashRebate>(0.8);
else if (type == "return300_100")
return std::make_unique<CashReturn>(300, 100);
else
return std::make_unique<CashNormal>();
}
};
// 客户端简化写法
CashContext ctx(CashStrategyFactory::createStrategy("rebate8"));
C++ 简单工厂模式完整示例
业务场景
四则计算器:加减乘除运算,由工厂统一创建运算对象,客户端只传运算符,不手动 new 具体运算类。
模式角色
- 抽象产品 Product:
Operation运算基类,定义计算接口 - 具体产品 ConcreteProduct:
Add、Sub、Mul、Div加减乘除 - 工厂 Factory:
OperationFactory,静态方法根据标识创建对应运算实例 - 客户端 Client:只依赖工厂+抽象产品,屏蔽所有创建细节
#include <iostream>
#include <memory>
// ====================== 1. 抽象产品:运算基类 ======================
class Operation
{
public:
virtual ~Operation() = default;
virtual double calc(double a, double b) const = 0;
};
// ====================== 2. 具体产品:四则运算 ======================
// 加法
class Add : public Operation
{
public:
double calc(double a, double b) const override
{
return a + b;
}
};
// 减法
class Sub : public Operation
{
public:
double calc(double a, double b) const override
{
return a - b;
}
};
// 乘法
class Mul : public Operation
{
public:
double calc(double a, double b) const override
{
return a * b;
}
};
// 除法
class Div : public Operation
{
public:
double calc(double a, double b) const override
{
if (b == 0)
{
std::cerr << "除数不能为0!" << std::endl;
return 0;
}
return a / b;
}
};
// ====================== 3. 简单工厂:负责创建运算对象 ======================
class OperationFactory
{
public:
// 静态工厂方法:根据运算符创建对应运算实例
static std::unique_ptr<Operation> createOperation(char op)
{
switch (op)
{
case '+':
return std::make_unique<Add>();
case '-':
return std::make_unique<Sub>();
case '*':
return std::make_unique<Mul>();
case '/':
return std::make_unique<Div>();
default:
std::cerr << "不支持的运算符!" << std::endl;
return nullptr;
}
}
};
// ====================== 4. 客户端调用 ======================
int main()
{
double num1 = 10, num2 = 2;
// 客户端只传入运算符,完全不用 new Add/Sub/Mul/Div
auto opAdd = OperationFactory::createOperation('+');
std::cout << "10 + 2 = " << opAdd->calc(num1, num2) << std::endl;
auto opSub = OperationFactory::createOperation('-');
std::cout << "10 - 2 = " << opSub->calc(num1, num2) << std::endl;
auto opMul = OperationFactory::createOperation('*');
std::cout << "10 * 2 = " << opMul->calc(num1, num2) << std::endl;
auto opDiv = OperationFactory::createOperation('/');
std::cout << "10 / 2 = " << opDiv->calc(num1, num2) << std::endl;
return 0;
}
运行输出
10 + 2 = 12
10 - 2 = 8
10 * 2 = 20
10 / 2 = 5
核心特点(简单工厂标志性特征)
- 创建逻辑全部收拢在工厂
所有new操作都在createOperation内部,客户端看不到具体子类; - 违反开闭原则
如果新增「取余%」运算,必须修改工厂里的switch分支,改动原有代码; - 工厂是唯一创建入口
客户端只能通过工厂获取对象,无权直接实例化具体产品; - 关注点:对象创建
只解决“如何生产对象”,不负责对象的业务执行。
和之前策略模式的核心对比(结合两份代码)
- 简单工厂:工厂负责 new 对象,客户端只拿抽象产品直接调用方法;没有 Context 上下文;
- 策略模式:客户端自己 new 策略对象,注入 Context;Context 只使用策略,绝不创建策略;
- 组合用法:可以把简单工厂嵌入策略模式,由工厂帮客户端创建策略,简化调用。
1016



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



