Java 设计模式------工厂模式:从原理到实战的系统指南
工厂模式是创建型设计模式的基础,核心思想是将对象的创建逻辑与使用逻辑分离 ,通过工厂类统一封装对象实例化过程,避免直接使用new
操作。它虽结构简单,但在降低代码耦合、提升可维护性上作用显著,是企业级开发中高频使用的模式之一。
文章目录
- [Java 设计模式------工厂模式:从原理到实战的系统指南](#Java 设计模式——工厂模式:从原理到实战的系统指南)
-
- 一、核心原理:为什么需要工厂模式?
-
- [1. 传统对象创建的痛点](#1. 传统对象创建的痛点)
- [2. 工厂模式的解决方案](#2. 工厂模式的解决方案)
- 二、工厂模式的三种实现方式
-
- [1. 简单工厂模式(静态工厂):单一工厂生产所有产品](#1. 简单工厂模式(静态工厂):单一工厂生产所有产品)
- [2. 工厂方法模式:一个产品对应一个工厂](#2. 工厂方法模式:一个产品对应一个工厂)
- [3. 抽象工厂模式:一个工厂生产多个关联产品](#3. 抽象工厂模式:一个工厂生产多个关联产品)
- 三、三种工厂模式的对比与选型
- 四、工厂模式的框架应用与实战技巧
-
- [1. 框架中的工厂模式](#1. 框架中的工厂模式)
-
- [(1)Spring 中的工厂模式](#(1)Spring 中的工厂模式)
- [(2)JDK 中的工厂模式](#(2)JDK 中的工厂模式)
- [2. 实战技巧](#2. 实战技巧)
- 五、避坑指南
- 六、总结
一、核心原理:为什么需要工厂模式?
1. 传统对象创建的痛点
直接通过new
关键字创建对象,会导致 "创建逻辑" 与 "使用逻辑" 强耦合,存在以下问题:
-
耦合度高:使用方需了解对象的创建细节(如构造参数、依赖关系),若对象创建逻辑修改(如新增参数),所有使用处需同步修改;
-
代码冗余:多个地方创建同一类型对象时,重复编写创建逻辑,违反 "单一职责原则";
-
扩展性差 :新增同类对象(如新增手机品牌)时,需在所有使用处添加
if-else
判断,违反 "开闭原则"。
以手机对象创建为例,传统写法的问题如下:
java
// 直接new对象,耦合创建逻辑
Phone phone;
if (name.equals("华为")) {
phone = new HuaWeiPhoneImpl(4, 21); // 需知道构造参数含义
} else if (name.equals("小米")) {
phone = new XiaoMiPhoneImpl(4, 21);
} else {
phone = null;
}
2. 工厂模式的解决方案
工厂模式通过引入 "工厂类" 作为中间层,承担对象创建的职责,实现:
-
解耦:使用方仅依赖工厂接口,无需关心对象创建细节;
-
复用:创建逻辑集中在工厂,避免代码冗余;
-
扩展:新增对象类型时,仅需扩展工厂,无需修改现有代码。
二、工厂模式的三种实现方式
根据复杂度和扩展性,工厂模式分为简单工厂模式 、工厂方法模式 、抽象工厂模式,三者适用场景逐步递进。
1. 简单工厂模式(静态工厂):单一工厂生产所有产品
核心逻辑
通过一个静态工厂类,根据输入参数(如产品类型)动态创建对应产品对象,无需创建工厂实例。
实战案例:手机生产工厂
(1)产品接口与实现类
java
// 手机接口(产品抽象)
public interface Phone {
String callUp(); // 核心功能:打电话
}
// 华为手机实现类(具体产品)
public class HuaWeiPhoneImpl implements Phone {
private Integer cpuCount;
private Integer memoryStorage;
// 构造函数(封装创建细节)
public HuaWeiPhoneImpl(Integer cpuCount, Integer memoryStorage) {
this.cpuCount = cpuCount;
this.memoryStorage = memoryStorage;
}
@Override
public String callUp() {
return "华为手机打电话(CPU:" + cpuCount + "核,内存:" + memoryStorage + "GB)";
}
}
// 小米手机实现类(具体产品)
public class XiaoMiPhoneImpl implements Phone {
private Integer cpuCount;
private Integer memoryStorage;
public XiaoMiPhoneImpl(Integer cpuCount, Integer memoryStorage) {
this.cpuCount = cpuCount;
this.memoryStorage = memoryStorage;
}
@Override
public String callUp() {
return "小米手机打电话(CPU:" + cpuCount + "核,内存:" + memoryStorage + "GB)";
}
}
(2)简单工厂类
java
// 手机静态工厂(封装所有产品的创建逻辑)
public class PhoneFactory {
// 静态方法:根据品牌参数创建对应手机
public static Phone createPhone(String brand) {
switch (brand) {
case "华为":
return new HuaWeiPhoneImpl(8, 256); // 固定创建参数,隐藏细节
case "小米":
return new XiaoMiPhoneImpl(8, 256);
default:
throw new IllegalArgumentException("不支持的手机品牌:" + brand);
}
}
}
(3)使用方调用
java
// 控制器(使用方,无需关心创建细节)
@RestController
@RequestMapping("product")
public class ProductController {
@GetMapping("phone")
public String getPhone(String brand) {
// 仅调用工厂方法,无需new对象
Phone phone = PhoneFactory.createPhone(brand);
return phone.callUp();
}
}
(4)测试结果
调用/product/phone?brand=华为
,返回:
text
华为手机打电话(CPU:8核,内存:256GB)
优缺点与适用场景
-
优点:实现简单,无需创建工厂实例,适合快速开发;
-
缺点 :工厂类与产品类型强耦合,新增产品需修改工厂
switch
逻辑,违反 "开闭原则"; -
适用场景:产品类型少、变化少的简单场景(如工具类创建、配置对象生成)。
2. 工厂方法模式:一个产品对应一个工厂
核心逻辑
定义 "工厂接口",每个具体产品对应一个 "具体工厂",通过工厂接口的多态性实现产品创建,解决简单工厂的扩展问题。
实战案例:手机品牌工厂
(1)工厂接口与具体工厂
java
// 手机工厂接口(工厂抽象)
public interface PhoneFactory {
Phone createPhone(); // 工厂方法:创建对应品牌手机
}
// 华为手机工厂(具体工厂)
public class HuaWeiFactoryImpl implements PhoneFactory {
@Override
public Phone createPhone() {
// 封装华为手机的创建细节
return new HuaWeiPhoneImpl(8, 256);
}
}
// 小米手机工厂(具体工厂)
public class XiaoMiFactoryImpl implements PhoneFactory {
@Override
public Phone createPhone() {
return new XiaoMiPhoneImpl(8, 256);
}
}
(2)使用方调用(结合工厂选择逻辑)
java
@RestController
@RequestMapping("product")
public class ProductController {
@GetMapping("phone")
public String getPhone(String brand) {
// 根据品牌选择对应工厂(可抽离为工厂选择器,避免if-else冗余)
PhoneFactory factory;
if (brand.equals("华为")) {
factory = new HuaWeiFactoryImpl();
} else if (brand.equals("小米")) {
factory = new XiaoMiFactoryImpl();
} else {
throw new IllegalArgumentException("不支持的手机品牌:" + brand);
}
// 工厂创建产品
Phone phone = factory.createPhone();
return phone.callUp();
}
}
优缺点与适用场景
-
优点:新增产品时,仅需新增 "具体产品类" 和 "具体工厂类",无需修改现有代码,符合 "开闭原则";
-
缺点:产品与工厂一一对应,产品类型过多时会导致 "工厂泛滥",增加代码复杂度;
-
适用场景:产品类型较多但单一维度扩展(如仅新增品牌,不新增产品品类)的场景。
3. 抽象工厂模式:一个工厂生产多个关联产品
核心逻辑
定义 "抽象工厂接口",每个具体工厂负责生产一组关联产品(如同一品牌的手机、路由器),解决多品类产品的统一创建问题。
实战案例:品牌产品族工厂
(1)新增关联产品接口(路由器)
java
// 路由器接口(关联产品抽象)
public interface Router {
String receiveData(); // 核心功能:接收网络数据
}
// 华为路由器实现类(具体关联产品)
public class HuaWeiRouterImpl implements Router {
private Integer networkSpeed; // 网速(Mbps)
public HuaWeiRouterImpl(Integer networkSpeed) {
this.networkSpeed = networkSpeed;
}
@Override
public String receiveData() {
return "华为路由器接收数据(网速:" + networkSpeed + "Mbps)";
}
}
// 小米路由器实现类(具体关联产品)
public class XiaoMiRouterImpl implements Router {
private Integer networkSpeed;
public XiaoMiRouterImpl(Integer networkSpeed) {
this.networkSpeed = networkSpeed;
}
@Override
public String receiveData() {
return "小米路由器接收数据(网速:" + networkSpeed + "Mbps)";
}
}
(2)抽象工厂接口与具体工厂
java
// 产品族工厂接口(抽象工厂,生产一组关联产品)
public interface BrandFactory {
Phone createPhone(); // 生产手机
Router createRouter(); // 生产路由器
}
// 华为产品族工厂(具体工厂,生产华为的所有产品)
public class HuaWeiBrandFactory implements BrandFactory {
@Override
public Phone createPhone() {
return new HuaWeiPhoneImpl(8, 256);
}
@Override
public Router createRouter() {
return new HuaWeiRouterImpl(3000); // 华为路由器网速3000Mbps
}
}
// 小米产品族工厂(具体工厂,生产小米的所有产品)
public class XiaoMiBrandFactory implements BrandFactory {
@Override
public Phone createPhone() {
return new XiaoMiPhoneImpl(8, 256);
}
@Override
public Router createRouter() {
return new XiaoMiRouterImpl(2500); // 小米路由器网速2500Mbps
}
}
(3)使用方调用
java
@RestController
@RequestMapping("product")
public class ProductController {
// 获取品牌的手机
@GetMapping("phone")
public String getPhone(String brand) {
BrandFactory factory = getBrandFactory(brand);
Phone phone = factory.createPhone();
return phone.callUp();
}
// 获取品牌的路由器
@GetMapping("router")
public String getRouter(String brand) {
BrandFactory factory = getBrandFactory(brand);
Router router = factory.createRouter();
return router.receiveData();
}
// 抽取工厂选择逻辑,避免代码冗余
private BrandFactory getBrandFactory(String brand) {
switch (brand) {
case "华为":
return new HuaWeiBrandFactory();
case "小米":
return new XiaoMiBrandFactory();
default:
throw new IllegalArgumentException("不支持的品牌:" + brand);
}
}
}
(4)测试结果
调用/product/router?brand=华为
,返回:
text
华为路由器接收数据(网速:3000Mbps)
优缺点与适用场景
-
优点:统一管理同一产品族的创建,确保产品间的兼容性;新增产品族时,仅需新增具体工厂,扩展性强;
-
缺点:新增产品品类(如新增 "智能手表")时,需修改抽象工厂接口及所有具体工厂,违反 "开闭原则";
-
适用场景:多品类、多品牌的产品体系(如电子设备、家居产品),且产品族内关联紧密的场景。
三、三种工厂模式的对比与选型
对比维度 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|---|
核心思想 | 单一工厂生产所有产品 | 一个产品对应一个工厂 | 一个工厂生产一组关联产品 |
工厂数量 | 1 个(静态工厂) | N 个(与产品数量一致) | M 个(与产品族数量一致) |
扩展性(新增产品) | 需修改工厂逻辑,违反开闭原则 | 新增产品 + 工厂,符合开闭原则 | 需修改抽象工厂,违反开闭原则 |
代码复杂度 | 低 | 中 | 高 |
适用场景 | 产品少、变化少 | 产品多、单一品类扩展 | 多品类、产品族关联紧密 |
典型案例 | 工具类创建、配置对象 | 单一品类多品牌(如手机) | 多品类产品族(如电子设备) |
选型建议
-
快速开发 / 简单场景:优先用简单工厂模式(如工具类、配置类);
-
单一品类扩展:用工厂方法模式(如仅新增手机品牌,不新增品类);
-
多品类产品族:用抽象工厂模式(如电子设备品牌,包含手机、路由器、手表)。
四、工厂模式的框架应用与实战技巧
1. 框架中的工厂模式
(1)Spring 中的工厂模式
-
BeanFactory :简单工厂模式的体现,通过
getBean()
方法根据 Bean 名称 / 类型创建 Bean 对象; -
FactoryBean :工厂方法模式的体现,每个
FactoryBean
负责创建特定类型的 Bean(如SqlSessionFactoryBean
创建SqlSessionFactory
); -
ApplicationContext:抽象工厂模式的扩展,管理 Bean 的生命周期,同时提供事件发布、资源加载等多维度功能。
(2)JDK 中的工厂模式
-
Calendar.getInstance() :简单工厂模式,根据时区 / 地区创建不同的
Calendar
实例; -
Connection :抽象工厂模式,
DriverManager
根据 URL 创建不同数据库的Connection
实例(如 MySQL、Oracle)。
2. 实战技巧
(1)消除工厂选择的if-else
通过 "工厂映射表" 替代if-else
,提升扩展性:
java
// 工厂映射表(初始化时注册所有工厂)
private Map<String, BrandFactory> factoryMap;
// 初始化工厂映射
@PostConstruct
public void initFactoryMap() {
factoryMap = new HashMap<>();
factoryMap.put("华为", new HuaWeiBrandFactory());
factoryMap.put("小米", new XiaoMiBrandFactory());
}
// 通过映射表获取工厂,无if-else
private BrandFactory getBrandFactory(String brand) {
BrandFactory factory = factoryMap.get(brand);
if (factory == null) {
throw new IllegalArgumentException("不支持的品牌:" + brand);
}
return factory;
}
(2)工厂的单例化
具体工厂类通常无状态,可通过单例模式避免重复创建:
java
// 华为工厂单例
public class HuaWeiBrandFactory implements BrandFactory {
// 私有构造函数
private HuaWeiBrandFactory() {}
// 静态内部类实现单例
private static class SingletonHolder {
private static final HuaWeiBrandFactory INSTANCE = new HuaWeiBrandFactory();
}
public static HuaWeiBrandFactory getInstance() {
return SingletonHolder.INSTANCE;
}
// 工厂方法...
}
(3)产品创建的复杂逻辑封装
若产品创建需依赖其他服务(如数据库查询、配置加载),可在工厂中注入依赖:
java
// 依赖注入的工厂(Spring环境)
@Service
public class HuaWeiBrandFactory implements BrandFactory {
// 注入配置服务
@Autowired
private DeviceConfigService configService;
@Override
public Phone createPhone() {
// 从配置服务获取参数,而非硬编码
DeviceConfig config = configService.getConfig("huawei.phone");
return new HuaWeiPhoneImpl(config.getCpuCount(), config.getMemoryStorage());
}
// ...
}
五、避坑指南
-
避免过度设计:简单场景用简单工厂即可,无需强行使用工厂方法或抽象工厂,增加不必要的复杂度;
-
工厂职责单一:工厂仅负责对象创建,不包含业务逻辑(如产品的使用、数据处理),符合 "单一职责原则";
-
注意线程安全:若工厂中存在共享状态(如缓存、计数器),需添加同步锁或使用线程安全的容器;
-
Spring 环境下优先用依赖注入 :无需手动创建工厂实例,通过
@Autowired
注入工厂,利用 Spring 管理生命周期。
六、总结
工厂模式的核心是 "分离创建与使用",三种实现方式分别对应不同复杂度的场景:
-
简单工厂模式是 "入门款",适合快速落地;
-
工厂方法模式是 "进阶款",适合单一品类扩展;
-
抽象工厂模式是 "高级款",适合多品类产品族。
在实际开发中,需根据产品复杂度、扩展需求选择合适的模式,同时结合框架特性(如 Spring 的依赖注入)简化实现,避免过度设计。掌握工厂模式,不仅能提升代码的可维护性,更能理解框架底层的设计思想(如 Spring、JDK 的工厂应用),为架构设计打下基础。