简单工厂设计模式

前言

在日常开发中,当我们需要根据不同参数创建不同对象时,代码很容易变成一堆 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. 定义抽象接口

public interface ILLMClient {
    String generateText(String prompt);
}

2. 实现具体产品

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. 构建简单工厂(核心所在)

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. 客户端调用

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 炼狱,每次修改都有可能不小心改错其他分支,导致线上故障。

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

模拟发奖多种商品,bugstack虫洞栈

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

本文代码仓库: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 面条代码

image-20260415214527847

5.3.2 简单工厂

image-20260415214724460

5.4 类图对比

5.4.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.4.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.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炼狱”,维护成本随产品种类增加而指数级上升。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐