设计模式02-工厂模式
2026/9/6 3:52:58 网站建设 项目流程

工厂模式:把"创建对象"收进一个入口

从一个真实的痛点出发:业务代码里的 if-else 分发,以及新增渠道时为什么总要改核心代码。

一、痛点:new 和 if-else 把代码焊死了

假设我们有一个文件上传模块,支持三种存储后端:本地磁盘、阿里云 OSS、AWS S3。最直白的写法是这样:

publicStringupload(Stringfile,StringstorageType){if("local".equals(storageType)){returnnewLocalStorage().save(file);}elseif("oss".equals(storageType)){returnnewOssStorage().save(file);}elseif("s3".equals(storageType)){returnnewS3Storage().save(file);}thrownewIllegalArgumentException("未知存储类型: "+storageType);}

这段代码的问题不是"不能用",而是"经不起变化":

  1. 开闭原则被违反:每新增一种存储后端(比如七牛云),就要回来改这段核心代码——对修改不封闭;
  2. 创建逻辑散落:调用方必须知道每个实现类的名字和构造细节;
  3. 调用方被绑定:业务代码与具体实现类强耦合,换实现、做 Mock 都很痛苦。

工厂模式的回答是:把"创建谁"这个决策收口到一个专门的工厂类里,调用方只面向接口

二、简单工厂:一个入口,屏蔽创建细节

第一步改造:抽出统一接口,用工厂类承接创建逻辑。

// 统一存储接口publicinterfaceStorageService{Stringsave(Stringfile);}
publicclassLocalStorageimplementsStorageService{@OverridepublicStringsave(Stringfile){return"local://"+file;}}// OssStorage、S3Storage 实现同构,略
importjava.util.HashMap;importjava.util.Map;importjava.util.function.Supplier;/** * 简单工厂:唯一的创建入口 * 用注册表替代 if-else 链,新增渠道时只动这一个类 */publicclassStorageFactory{privatestaticfinalMap<String,Supplier<StorageService>>REGISTRY=newHashMap<>();static{REGISTRY.put("local",LocalStorage::new);REGISTRY.put("oss",OssStorage::new);REGISTRY.put("s3",S3Storage::new);}publicstaticStorageServicecreate(Stringtype){Supplier<StorageService>supplier=REGISTRY.get(type);if(supplier==null){thrownewIllegalArgumentException("不支持的存储类型: "+type);}returnsupplier.get();}}

业务层彻底与实现类解耦:

publicclassFileService{publicStringupload(Stringfile,StringstorageType){StorageServicestorage=StorageFactory.create(storageType);returnstorage.save(file);}}

现在FileService不知道、也不需要知道 LocalStorage 和 OssStorage 的存在。它只依赖两个抽象:StorageService接口和工厂的 create 方法。

三、简单工厂的边界

注意:简单工厂把 if-else 从业务代码挪进了工厂类,没有消灭 if-else,只是把它集中了。这带来两个效果:

  • 好处:创建逻辑单点管理,业务代码干净了;
  • 代价:新增渠道仍需修改工厂类(加一行注册),严格说没有完全满足开闭原则。

如果"新增渠道不改任何现有类"是硬要求,就需要工厂方法模式了。

四、工厂方法:一个产品,一个工厂

工厂方法的思路是:把"创建"也抽象成接口,每种产品对应一个独立工厂类

// 工厂接口(GoF 术语中称为 Creator)publicinterfaceStorageCreator{StorageServicecreate();}
publicclassLocalStorageCreatorimplementsStorageCreator{@OverridepublicStorageServicecreate(){returnnewLocalStorage();}}// OssStorageCreator、S3StorageCreator 同构,略

调用方持有工厂接口,新增 S3 时写一个S3StorageCreator即可,现有类零改动

publicclassFileUploader{privatefinalStorageCreatorcreator;publicFileUploader(StorageCreatorcreator){this.creator=creator;}publicStringupload(Stringfile){returncreator.create().save(file);}}

代价也很明显:类的数量翻倍——每个产品多一个工厂类。产品类型少时显得繁琐。

五、两种工厂怎么选

维度简单工厂工厂方法
创建逻辑的位置集中在唯一工厂类分散到各产品专属工厂
新增产品改工厂类(加一行注册)新增工厂类,现有代码零修改
类数量每个产品多一个类
开闭原则部分满足完全满足
适用场景产品类型稳定、集中管理产品类型持续扩张、强调扩展性

工程上的务实结论:多数场景用简单工厂 + 注册表就够;当产品线明显会持续扩张、且团队对"改现有代码"很敏感时,升级到工厂方法。

六、工厂模式没回答的问题

工厂模式解决的是"创建哪个对象",它不解决"创建完之后,不同对象的行为差异怎么办"。

回到存储例子:三种存储后端的 save 逻辑完全不同——这个差异由多态解决,和工厂无关。如果业务里还有一类需求是"根据参数选择执行哪套行为、并随时可切换",那是策略模式的领地(见本系列第 3 篇)。

这也是工厂和策略经常被一起提问的原因:工厂管"造",策略管"用"

小结

工厂模式的价值一句话:创建入口收口,调用方面向接口。简单工厂集中管理,工厂方法彻底开闭。选哪个,取决于产品类型的扩张预期和团队对类数量的容忍度。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询