简单工厂设计模式

简单工厂设计模式

前言

在日常开发中,当我们需要根据不同参数创建不同对象时,代码很容易变成一堆 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炼狱",维护成本随产品种类增加而指数级上升。

相关推荐
你不是我我7 小时前
【Java 开发日记】HTTP3 性能更好,为什么内网微服务依然多用 HTTP2?HTTP2 内网优势是什么?
java·开发语言·微服务
雪碧聊技术8 小时前
大模型爆火!Java后端如何抓住Agent全栈开发的风口
java·大模型·agent·全栈开发
逻辑驱动的ken9 小时前
Java高频面试场景题25
java·开发语言·深度学习·面试·职场和发展
AI人工智能+电脑小能手10 小时前
【大白话说Java面试题】【Java基础篇】第32题:Java的异常处理机制是什么
java·开发语言·后端·面试
wdfk_prog11 小时前
正常关闭虚拟机时,不要点“关机”,而要点“关闭客户机”
linux·c语言·网络·ide·vscode
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ11 小时前
通过java后端代码来实现给word内容补充格式文本内容控件,以及 设置控件的标记和标题
java·c#·word
無限進步D12 小时前
Java 面向对象高级 接口
java·开发语言
逸Y 仙X13 小时前
文章二十七:ElasticSearch ES查询模板(Search Template)高效复用实战
java·大数据·数据库·elasticsearch·搜索引擎·全文检索
二哈赛车手13 小时前
新人笔记---Spring AI的Advisor以及其底层机制讲解(涉及源码),包含一些遇见的Spring AI的Advisor缺陷问题的解决方案
java·人工智能·spring boot·笔记·spring