简单工厂设计模式

简单工厂设计模式

前言

在日常开发中,当我们需要根据不同参数创建不同对象时,代码很容易变成一堆 if-else 的"面条代码"。

简单工厂模式(Static Factory Method) 正是为了解决这个问题而生:它把所有对象的创建逻辑集中到一个"大工厂"里,让调用方只需要传入一个类型参数,就能拿到统一的接口对象,无需关心具体是哪一个实现类。

本文从简单工厂模式的核心定义与UML结构出发,以两个场景对比"面条代码"与"简单工厂"两种写法:

  • LLM模型网关路由;
  • 营销发奖系统(优惠券、实物商品、爱奇艺卡三种不同发放逻辑)。

一、 核心定义

简单工厂模式又叫做静态工厂方法模式(Static Factory Method)

它的核心思想是:由一个专门的"大工厂"类,根据传入的参数,集中包含所有具体对象的创建逻辑,并返回它们的通用接口。

打个比方:简单工厂就像是一个全能的"自动售货机",你按 1,它给你掉下来可乐;你按 2,它给你掉下来雪碧。售货机(工厂)内部自己搞定了所有的判断逻辑。

二、 标准体系结构图 (UML)

角色说明

  1. 抽象产品(Product):定义规范,java中通常是接口。
  2. 具体产品(ConcreteProduct):实现规范的具体类,用于实现接口。
  3. 简单工厂(SimpleFactory):包含了必要的判断逻辑,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。

实现
实现
依赖抽象
实例化 (内部通过 if/switch)
实例化 (内部通过 if/switch)
<<Interface>>
Product
+execute() : void
ConcreteProductA
+execute() : void
ConcreteProductB
+execute() : void
SimpleFactory
+createProduct(type: String) : Product

三、 场景推演:LLM 模型网关路由

在开发大模型 Agent 或自进化框架时,底层通常需要接入不同的 LLM 提供商(如 OpenAI, Anthropic, DeepSeek)。

如果使用简单工厂模式来构建这个网关,代码会是这样的:

1. 定义抽象接口

Java 复制代码
public interface ILLMClient {
    String generateText(String prompt);
}

2. 实现具体产品

Java 复制代码
public class OpenAIClient implements ILLMClient {
    @Override
    public String generateText(String prompt) {
        return "Calling GPT-4 API: " + prompt;
    }
}

public class DeepSeekClient implements ILLMClient {
    @Override
    public String generateText(String prompt) {
        return "Calling DeepSeek-Coder API: " + prompt;
    }
}

3. 构建简单工厂(核心所在)

Java 复制代码
public class LLMFactory {
    // 通常设计为静态方法,或者像之前的 StoreFactory 一样提供一个实例方法
    public static ILLMClient createClient(String providerType) {
        // 核心特征:所有的实例化逻辑都集中在这个 if-else / switch 结构中
        if ("openai".equalsIgnoreCase(providerType)) {
            return new OpenAIClient();
        } else if ("deepseek".equalsIgnoreCase(providerType)) {
            return new DeepSeekClient();
        } else {
            throw new IllegalArgumentException("Unsupported LLM provider: " + providerType);
        }
    }
}

4. 客户端调用

Java 复制代码
public class AgentSystem {
    public void run() {
        // 调用方只需要知道名字,不需要自己去 new 对象
        ILLMClient client = LLMFactory.createClient("deepseek");
        String response = client.generateText("Analyze this SWE-bench issue...");
    }
}

四、 核心优缺点

理解设计模式的关键在于理解它的妥协。

4.1 核心优势

  1. 极度简单、快速见效 :它确确实实帮调用方(如 Controller 层或 Agent Core)把创建对象的脏活累活干了,调用方变得干净清爽,实现了一定程度上的解耦
  2. 职责明确:谁负责用对象,谁负责创建对象,分工清晰。

4.2 缺陷

简单工厂最大的问题在于它违背了"开闭原则(OCP)"

  • 假设你的框架现在需要接入一个新的模型 ClaudeClient,你除了要写一个新的类之外,还必须回头去修改 LLMFactory 里面的 if-else 代码
  • 如果系统非常庞大,有 50 种具体产品,这个工厂类的代码将会变成一个巨大的 switch 炼狱,每次修改都有可能不小心改错其他分支,导致线上故障。

五、实战演练:模拟发奖多种商品

实战演练的代码参考小傅哥博客

本文代码仓库:https://github.com/likerhood/CodeDesignWork#

5.1 需求分析

在营销、抽奖等业务场景中,系统往往需要向用户发放多种类型的奖品(如:优惠券、实物商品、第三方兑换卡等)。

  • 业务痛点 :每种奖品的发放逻辑、所需的参数、以及底层调用的外部接口(可能是不同部门或不同公司提供的 RPC/HTTP 接口)完全不一致
    • 优惠券 :需要 uId (用户ID), awardNumber (券码), bizId (防重流水号)。
    • 实物商品:需要极度详细的收货信息(姓名、电话、SKU、防重ID、收货地址等)。
    • 爱奇艺兑换卡 :仅需要用户的 手机号兑换码
  • 架构目标:对上层业务(如抽奖接口、前端调用)屏蔽底层的发奖差异,提供一个统一的、高内聚低耦合的发奖网关。

如图:描述了系统参与者(外部系统/用户)与发奖系统之间的功能边界。
外部依赖服务
发奖业务系统核心用例
业务客户都安
触发发奖请求
触发发奖请求
触发发奖请求
<> 调用发券接口
<> 调用发货接口
<> 调用发卡接口
触发方
发放优惠券奖品
发放实物商品
发放爱奇艺兑换卡
优惠券服务
物流商品服务
爱奇艺卡服务

5.2 发放奖品的请求体和响应体字段设计

AwardReq
-String uId %% 用户ID
-Integer awardType %% 1优惠券 2实物商品 3爱奇艺卡
-String bizId %% 幂等业务ID防重复
-String awardNumber %% 奖品编号(券号/SKU/卡ID)
-Map<String,String> extMap %% 扩展字段(收货地址等)
+getuId() : String
+getAwardType() : Integer
+getAwardNumber() : String
+getBizId() : String
+getExtMap() : Map
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 面条代码类图

对应仓库中项目模块:codedesign1.0-0
入参
出参
if awardType==1 直接 new
if awardType==2 直接 new
if awardType==3 直接 new
PrizeController
-Logger logger
+awardToUser(AwardReq req) : AwardRes
-queryUserName(uId) : String
-queryUserPhoneNumber(uId) : String
AwardReq
-String uId
-Integer awardType
-String awardNumber
-String bizId
-Map extMap
AwardRes
-String code
-String info
CouponService
+sendCoupon(uId, couponNumber, uuid) : CouponResult
GoodsService
+deliverGoods(DeliverReq) : Boolean
IQiYiCardService
+grantToken(mobile, cardId) : void

5.3.2 简单工厂类图

入参
出参
持有
创建返回
PrizeController
-Logger logger
-StoreFactory storeFactory
+awardToUser(AwardReq req) : AwardRes
StoreFactory
+getCommodityService(Integer) : ICommodity
+getCommodityService(Class) : ICommodity
<<interface>>
ICommodity
+sendCommodity(uId, commodityId, bizId, extMap) : void
CouponCommodityService
-CouponService couponService
+sendCommodity(...)
GoodsCommodityService
-GoodsService goodsService
+sendCommodity(...)
-queryUserName(uId) : String
-queryUserPhoneNumber(uId) : String
CardCommodityService
-IQiYiCardService iQiYiCardService
+sendCommodity(...)
-queryUserMobile(uId) : String
AwardReq
-String uId
-Integer awardType
-String awardNumber
-String bizId
-Map extMap
AwardRes
-String code
-String info

5.4 简单体系结构图对比

5.4.1 面条代码

外部服务 4.0-0
业务层 单一Controller
客户端
AwardReq
if type==1
if type==2
if type==3
AwardRes
ApiTest
PrizeController

⚠️ 所有逻辑都在这里
CouponService
GoodsService
IQiYiCardService

5.4.2 简单工厂

外部服务 4.0-0
实现层
业务层 分离职责
客户端
AwardReq
返回ICommodity
实现
实现
实现
AwardRes
ApiTest
PrizeController

只负责调用工厂和返回结果
StoreFactory

只负责创建对象
ICommodity

统一接口
CouponCommodityService
GoodsCommodityService
CardCommodityService
CouponService
GoodsService
IQiYiCardService

5.5 时序图对比

5.5.1 面条代码

GoodsService PrizeController ApiTest GoodsService PrizeController ApiTest if type==1 → false else if type==2 → true awardToUser(AwardReq{type=2}) new GoodsService() new DeliverReq() 手动组装7个字段 queryUserName() / queryUserPhoneNumber() deliverGoods(deliverReq) true AwardRes("0000","发放成功")

5.5.2 简单工厂

GoodsService GoodsCommodityService StoreFactory PrizeController ApiTest GoodsService GoodsCommodityService StoreFactory PrizeController ApiTest awardToUser(AwardReq{type=2}) getCommodityService(awardType=2) GoodsCommodityService实例 sendCommodity(uId, sku, bizId, extMap) 组装DeliverReq(职责在这里) deliverGoods(deliverReq) true 正常返回 AwardRes("0000","发放成功")

总结

简单工厂模式又称静态工厂方法模式,其核心是由一个专门的工厂类(SimpleFactory / StoreFactory)根据传入的参数(如字符串或枚举),集中完成所有具体产品的实例化,并统一返回抽象产品接口(Product / ICommodity)。

核心角色包括:

  • 抽象产品(Product / ICommodity)
  • 具体产品(ConcreteProductA/B / CouponCommodityService、GoodsCommodityService、CardCommodityService)
  • 简单工厂(负责if/switch判断与new对象)

典型应用场景

  • LLM提供商路由(OpenAI、DeepSeek、Claude等)
  • 营销发奖系统(不同奖品类型对应不同外部服务)

优点 :极简、职责清晰、调用方代码干净,快速实现一定程度的解耦。 最大缺陷:违背开闭原则(OCP),每新增一种产品都必须修改工厂类的判断逻辑,容易形成"巨型switch炼狱",维护成本随产品种类增加而指数级上升。

相关推荐
星晨雪海21 小时前
Lombok 注解使用场景终极总结
java·数据库·mysql
Stella Blog21 小时前
狂神Java基础学习笔记Day03
java·笔记·学习
zopple1 天前
四大编程语言对比:PHP、Python、Java与易语言
java·python·php
逍遥德1 天前
Java 锁(线程间)和数据库锁(事务间)对比详解
java·数据库·sql·高并发·锁机制
gwjcloud1 天前
Docker详解
java·docker·容器
河阿里1 天前
Java-JWT令牌技术深度指南
java·开发语言
WiChP1 天前
【V0.1B6】从零开始的2D游戏引擎开发之路
java·log4j·游戏引擎
leaves falling1 天前
C/C++ 的内存管理,函数栈帧详讲
java·c语言·c++
文静小土豆1 天前
Java 应用上 K8s 全指南:从部署到治理的生产级实践
java·开发语言·kubernetes
ZHANG13HAO1 天前
Android 13 特权应用(Android Studio 开发)调用 AOSP 隐藏 API 完整教程
android·ide·android studio