设计模式之简单工厂设计模式
简单工厂设计模式
前言
在日常开发中,当我们需要根据不同参数创建不同对象时,代码很容易变成一堆 if-else 的“面条代码”。
**简单工厂模式(Static Factory Method)**正是为了解决这个问题而生:它把所有对象的创建逻辑集中到一个“大工厂”里,让调用方只需要传入一个类型参数,就能拿到统一的接口对象,无需关心具体是哪一个实现类。
本文从简单工厂模式的核心定义与UML结构出发,以两个场景对比“面条代码”与“简单工厂”两种写法:
- LLM模型网关路由;
- 营销发奖系统(优惠券、实物商品、爱奇艺卡三种不同发放逻辑)。
一、 核心定义
简单工厂模式又叫做静态工厂方法模式(Static Factory Method)。
它的核心思想是:由一个专门的“大工厂”类,根据传入的参数,集中包含所有具体对象的创建逻辑,并返回它们的通用接口。
打个比方:简单工厂就像是一个全能的“自动售货机”,你按 1,它给你掉下来可乐;你按 2,它给你掉下来雪碧。售货机(工厂)内部自己搞定了所有的判断逻辑。
二、 标准体系结构图 (UML)
角色说明:
- 抽象产品(Product):定义规范,java中通常是接口。
- 具体产品(ConcreteProduct):实现规范的具体类,用于实现接口。
- 简单工厂(SimpleFactory):包含了必要的判断逻辑,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。
classDiagram
direction BT
class Product {
<<Interface>>
+execute() void
}
class ConcreteProductA {
+execute() void
}
class ConcreteProductB {
+execute() void
}
class SimpleFactory {
+createProduct(type: String) Product
}
ConcreteProductA ..|> Product : 实现
ConcreteProductB ..|> Product : 实现
SimpleFactory ..> Product : 依赖抽象
SimpleFactory ..> ConcreteProductA : 实例化 (内部通过 if/switch)
SimpleFactory ..> ConcreteProductB : 实例化 (内部通过 if/switch)
%% 直接应用样式避免解析错误
style Product fill:#E8F0FE,stroke:#1A73E8,stroke-width:1px
style SimpleFactory fill:#FCE8E6,stroke:#D93025,stroke-width:1px
style ConcreteProductA fill:#E6F4EA,stroke:#1E8E3E,stroke-width:1px
style ConcreteProductB fill:#E6F4EA,stroke:#1E8E3E,stroke-width:1px
三、 场景推演:LLM 模型网关路由
在开发大模型 Agent 或自进化框架时,底层通常需要接入不同的 LLM 提供商(如 OpenAI, Anthropic, DeepSeek)。
如果使用简单工厂模式来构建这个网关,代码会是这样的:
1. 定义抽象接口
1 | public interface ILLMClient { |
2. 实现具体产品
1 | public class OpenAIClient implements ILLMClient { |
3. 构建简单工厂(核心所在)
1 | public class LLMFactory { |
4. 客户端调用
1 | public class AgentSystem { |
四、 核心优缺点
理解设计模式的关键在于理解它的妥协。
4.1 核心优势
- 极度简单、快速见效:它确确实实帮调用方(如 Controller 层或 Agent Core)把创建对象的脏活累活干了,调用方变得干净清爽,实现了一定程度上的解耦。
- 职责明确:谁负责用对象,谁负责创建对象,分工清晰。
4.2 缺陷
简单工厂最大的问题在于它违背了“开闭原则(OCP)”。
- 假设你的框架现在需要接入一个新的模型
ClaudeClient,你除了要写一个新的类之外,还必须回头去修改LLMFactory里面的if-else代码。 - 如果系统非常庞大,有 50 种具体产品,这个工厂类的代码将会变成一个巨大的
switch炼狱,每次修改都有可能不小心改错其他分支,导致线上故障。
五、实战演练:模拟发奖多种商品

实战演练的代码参考小傅哥博客
5.1 需求分析
在营销、抽奖等业务场景中,系统往往需要向用户发放多种类型的奖品(如:优惠券、实物商品、第三方兑换卡等)。
- 业务痛点:每种奖品的发放逻辑、所需的参数、以及底层调用的外部接口(可能是不同部门或不同公司提供的 RPC/HTTP 接口)完全不一致。
- 优惠券:需要
uId(用户ID),awardNumber(券码),bizId(防重流水号)。 - 实物商品:需要极度详细的收货信息(姓名、电话、SKU、防重ID、收货地址等)。
- 爱奇艺兑换卡:仅需要用户的
手机号和兑换码。
- 优惠券:需要
- 架构目标:对上层业务(如抽奖接口、前端调用)屏蔽底层的发奖差异,提供一个统一的、高内聚低耦合的发奖网关。
如图:描述了系统参与者(外部系统/用户)与发奖系统之间的功能边界。
graph TB
%% 外部角色定义
subgraph 业务客户都安
OpsSystem([触发方])
end
%% 系统内部核心用例
subgraph 发奖业务系统核心用例
UC1(发放优惠券奖品)
UC2(发放实物商品)
UC3(发放爱奇艺兑换卡)
end
%% 第三方或下游服务角色
subgraph 外部依赖服务
CouponSvc([优惠券服务])
GoodsSvc([物流商品服务])
IQiYiSvc([爱奇艺卡服务])
end
%% 触发方与用例的关系
OpsSystem -->|"触发发奖请求"| UC1
OpsSystem -->|"触发发奖请求"| UC2
OpsSystem -->|"触发发奖请求"| UC3
%% 用例与外部依赖的关系
UC1 -.->|"<<include>> 调用发券接口"| CouponSvc
UC2 -.->|"<<include>> 调用发货接口"| GoodsSvc
UC3 -.->|"<<include>> 调用发卡接口"| IQiYiSvc
%% 样式美化
classDef actor fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
classDef usecase fill:#fff3e0,stroke:#e65100,stroke-width:2px;
class OpsSystem,CouponSvc,GoodsSvc,IQiYiSvc actor;
class UC1,UC2,UC3 usecase;
5.2 发放奖品的请求体和响应体字段设计
classDiagram
class AwardReq {
-String uId %% 用户ID
-Integer awardType %% 1优惠券 2实物商品 3爱奇艺卡
-String awardNumber %% 奖品编号(券号/SKU/卡ID)
-String bizId %% 幂等业务ID防重复
-Map~String,String~ extMap %% 扩展字段(收货地址等)
+getuId() String
+getAwardType() Integer
+getAwardNumber() String
+getBizId() String
+getExtMap() Map
}
class AwardRes {
-String code %% 0000成功 0001失败
-String info %% 描述信息
+AwardRes(code, info)
+getCode() String
+getInfo() String
}
extMap 的 key 约定(发实物商品时必传):
| key | 含义 |
|---|---|
consigneeUserName |
收货人姓名 |
consigneeUserPhone |
收货人手机 |
consigneeUserAddress |
收货地址 |
5.3 简单体系结构图对比
5.3.1 面条代码

5.3.2 简单工厂

5.4 类图对比
5.4.1 面条代码类图
对应仓库中项目模块:codedesign1.0-0
classDiagram
class PrizeController {
-Logger logger
+awardToUser(AwardReq req) AwardRes
-queryUserName(uId) String
-queryUserPhoneNumber(uId) String
}
class AwardReq {
-String uId
-Integer awardType
-String awardNumber
-String bizId
-Map extMap
}
class AwardRes {
-String code
-String info
}
class CouponService {
+sendCoupon(uId, couponNumber, uuid) CouponResult
}
class GoodsService {
+deliverGoods(DeliverReq) Boolean
}
class IQiYiCardService {
+grantToken(mobile, cardId) void
}
PrizeController ..> AwardReq : 入参
PrizeController ..> AwardRes : 出参
PrizeController ..> CouponService : if awardType==1 直接 new
PrizeController ..> GoodsService : if awardType==2 直接 new
PrizeController ..> IQiYiCardService : if awardType==3 直接 new
5.4.2 简单工厂类图
classDiagram
class PrizeController {
-Logger logger
-StoreFactory storeFactory
+awardToUser(AwardReq req) AwardRes
}
class StoreFactory {
+getCommodityService(Integer) ICommodity
+getCommodityService(Class) ICommodity
}
class ICommodity {
<<interface>>
+sendCommodity(uId, commodityId, bizId, extMap) void
}
class CouponCommodityService {
-CouponService couponService
+sendCommodity(...)
}
class GoodsCommodityService {
-GoodsService goodsService
+sendCommodity(...)
-queryUserName(uId) String
-queryUserPhoneNumber(uId) String
}
class CardCommodityService {
-IQiYiCardService iQiYiCardService
+sendCommodity(...)
-queryUserMobile(uId) String
}
class AwardReq {
-String uId
-Integer awardType
-String awardNumber
-String bizId
-Map extMap
}
class AwardRes {
-String code
-String info
}
PrizeController ..> AwardReq : 入参
PrizeController ..> AwardRes : 出参
PrizeController --> StoreFactory : 持有
StoreFactory ..> ICommodity : 创建返回
ICommodity <|.. CouponCommodityService
ICommodity <|.. GoodsCommodityService
ICommodity <|.. CardCommodityService
5.5 时序图对比
5.5.1 面条代码
sequenceDiagram
participant T as ApiTest
participant PC as PrizeController
participant GS as GoodsService
T->>PC: awardToUser(AwardReq{type=2})
activate PC
Note over PC: if type==1 → false<br/>else if type==2 → true
PC->>PC: new GoodsService()
PC->>PC: new DeliverReq() 手动组7个字段
PC->>PC: queryUserName() / queryUserPhoneNumber()
PC->>GS: deliverGoods(deliverReq)
GS-->>PC: true
PC-->>T: AwardRes("0000","发放成功")
deactivate PC
5.5.2 简单工厂
sequenceDiagram
participant T as ApiTest
participant PC as PrizeController
participant SF as StoreFactory
participant GC as GoodsCommodityService
participant GS as GoodsService
T->>PC: awardToUser(AwardReq{type=2})
activate PC
PC->>SF: getCommodityService(awardType=2)
SF-->>PC: GoodsCommodityService实例
PC->>GC: sendCommodity(uId, sku, bizId, extMap)
activate GC
GC->>GC: 组装DeliverReq(职责在这里)
GC->>GS: deliverGoods(deliverReq)
GS-->>GC: true
GC-->>PC: 正常返回
deactivate GC
PC-->>T: AwardRes("0000","发放成功")
deactivate PC
总结
简单工厂模式又称静态工厂方法模式,其核心是由一个专门的工厂类(SimpleFactory / StoreFactory)根据传入的参数(如字符串或枚举),集中完成所有具体产品的实例化,并统一返回抽象产品接口(Product / ICommodity)。
核心角色包括:
- 抽象产品(Product / ICommodity)
- 具体产品(ConcreteProductA/B / CouponCommodityService、GoodsCommodityService、CardCommodityService)
- 简单工厂(负责if/switch判断与new对象)
典型应用场景:
- LLM提供商路由(OpenAI、DeepSeek、Claude等)
- 营销发奖系统(不同奖品类型对应不同外部服务)
优点:极简、职责清晰、调用方代码干净,快速实现一定程度的解耦。 最大缺陷:违背开闭原则(OCP),每新增一种产品都必须修改工厂类的判断逻辑,容易形成“巨型switch炼狱”,维护成本随产品种类增加而指数级上升。