Java 设计模式——工厂模式:从原理到实战的系统指南
Java 设计模式——工厂模式:从原理到实战的系统指南
工厂模式是创建型设计模式的基础,核心思想是将对象的创建逻辑与使用逻辑分离,通过工厂类统一封装对象实例化过程,避免直接使用new
操作。它虽结构简单,但在降低代码耦合、提升可维护性上作用显著,是企业级开发中高频使用的模式之一。
文章目录
- Java 设计模式——工厂模式:从原理到实战的系统指南
- 一、核心原理:为什么需要工厂模式?
- 1. 传统对象创建的痛点
- 2. 工厂模式的解决方案
- 二、工厂模式的三种实现方式
- 1. 简单工厂模式(静态工厂):单一工厂生产所有产品
- 核心逻辑
- 实战案例:手机生产工厂
- (1)产品接口与实现类
- (2)简单工厂类
- (3)使用方调用
- (4)测试结果
- 优缺点与适用场景
- 2. 工厂方法模式:一个产品对应一个工厂
- 核心逻辑
- 实战案例:手机品牌工厂
- (1)工厂接口与具体工厂
- (2)使用方调用(结合工厂选择逻辑)
- 优缺点与适用场景
- 3. 抽象工厂模式:一个工厂生产多个关联产品
- 核心逻辑
- 实战案例:品牌产品族工厂
- (1)新增关联产品接口(路由器)
- (2)抽象工厂接口与具体工厂
- (3)使用方调用
- (4)测试结果
- 优缺点与适用场景
- 三、三种工厂模式的对比与选型
- 选型建议
- 四、工厂模式的框架应用与实战技巧
- 1. 框架中的工厂模式
- (1)Spring 中的工厂模式
- (2)JDK 中的工厂模式
- 2. 实战技巧
- (1)消除工厂选择的`if-else`
- (2)工厂的单例化
- (3)产品创建的复杂逻辑封装
- 五、避坑指南
- 六、总结
一、核心原理:为什么需要工厂模式?
1. 传统对象创建的痛点
直接通过new
关键字创建对象,会导致 “创建逻辑” 与 “使用逻辑” 强耦合,存在以下问题:
-
耦合度高:使用方需了解对象的创建细节(如构造参数、依赖关系),若对象创建逻辑修改(如新增参数),所有使用处需同步修改;
-
代码冗余:多个地方创建同一类型对象时,重复编写创建逻辑,违反 “单一职责原则”;
-
扩展性差:新增同类对象(如新增手机品牌)时,需在所有使用处添加
if-else
判断,违反 “开闭原则”。
以手机对象创建为例,传统写法的问题如下:
// 直接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)产品接口与实现类
// 手机接口(产品抽象)
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;}@Overridepublic 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;}@Overridepublic String callUp() {return "小米手机打电话(CPU:" + cpuCount + "核,内存:" + memoryStorage + "GB)";}
}
(2)简单工厂类
// 手机静态工厂(封装所有产品的创建逻辑)
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)使用方调用
// 控制器(使用方,无需关心创建细节)
@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=华为
,返回:
华为手机打电话(CPU:8核,内存:256GB)
优缺点与适用场景
-
优点:实现简单,无需创建工厂实例,适合快速开发;
-
缺点:工厂类与产品类型强耦合,新增产品需修改工厂
switch
逻辑,违反 “开闭原则”; -
适用场景:产品类型少、变化少的简单场景(如工具类创建、配置对象生成)。
2. 工厂方法模式:一个产品对应一个工厂
核心逻辑
定义 “工厂接口”,每个具体产品对应一个 “具体工厂”,通过工厂接口的多态性实现产品创建,解决简单工厂的扩展问题。
实战案例:手机品牌工厂
(1)工厂接口与具体工厂
// 手机工厂接口(工厂抽象)
public interface PhoneFactory {Phone createPhone(); // 工厂方法:创建对应品牌手机
}// 华为手机工厂(具体工厂)
public class HuaWeiFactoryImpl implements PhoneFactory {@Overridepublic Phone createPhone() {// 封装华为手机的创建细节return new HuaWeiPhoneImpl(8, 256);}
}// 小米手机工厂(具体工厂)
public class XiaoMiFactoryImpl implements PhoneFactory {@Overridepublic Phone createPhone() {return new XiaoMiPhoneImpl(8, 256);}
}
(2)使用方调用(结合工厂选择逻辑)
@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)新增关联产品接口(路由器)
// 路由器接口(关联产品抽象)
public interface Router {String receiveData(); // 核心功能:接收网络数据
}// 华为路由器实现类(具体关联产品)
public class HuaWeiRouterImpl implements Router {private Integer networkSpeed; // 网速(Mbps)public HuaWeiRouterImpl(Integer networkSpeed) {this.networkSpeed = networkSpeed;}@Overridepublic String receiveData() {return "华为路由器接收数据(网速:" + networkSpeed + "Mbps)";}
}// 小米路由器实现类(具体关联产品)
public class XiaoMiRouterImpl implements Router {private Integer networkSpeed;public XiaoMiRouterImpl(Integer networkSpeed) {this.networkSpeed = networkSpeed;}@Overridepublic String receiveData() {return "小米路由器接收数据(网速:" + networkSpeed + "Mbps)";}
}
(2)抽象工厂接口与具体工厂
// 产品族工厂接口(抽象工厂,生产一组关联产品)
public interface BrandFactory {Phone createPhone(); // 生产手机Router createRouter(); // 生产路由器
}// 华为产品族工厂(具体工厂,生产华为的所有产品)
public class HuaWeiBrandFactory implements BrandFactory {@Overridepublic Phone createPhone() {return new HuaWeiPhoneImpl(8, 256);}@Overridepublic Router createRouter() {return new HuaWeiRouterImpl(3000); // 华为路由器网速3000Mbps}
}// 小米产品族工厂(具体工厂,生产小米的所有产品)
public class XiaoMiBrandFactory implements BrandFactory {@Overridepublic Phone createPhone() {return new XiaoMiPhoneImpl(8, 256);}@Overridepublic Router createRouter() {return new XiaoMiRouterImpl(2500); // 小米路由器网速2500Mbps}
}
(3)使用方调用
@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=华为
,返回:
华为路由器接收数据(网速: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
,提升扩展性:
// 工厂映射表(初始化时注册所有工厂)
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)工厂的单例化
具体工厂类通常无状态,可通过单例模式避免重复创建:
// 华为工厂单例
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)产品创建的复杂逻辑封装
若产品创建需依赖其他服务(如数据库查询、配置加载),可在工厂中注入依赖:
// 依赖注入的工厂(Spring环境)
@Service
public class HuaWeiBrandFactory implements BrandFactory {// 注入配置服务@Autowiredprivate DeviceConfigService configService;@Overridepublic Phone createPhone() {// 从配置服务获取参数,而非硬编码DeviceConfig config = configService.getConfig("huawei.phone");return new HuaWeiPhoneImpl(config.getCpuCount(), config.getMemoryStorage());}// ...
}
五、避坑指南
-
避免过度设计:简单场景用简单工厂即可,无需强行使用工厂方法或抽象工厂,增加不必要的复杂度;
-
工厂职责单一:工厂仅负责对象创建,不包含业务逻辑(如产品的使用、数据处理),符合 “单一职责原则”;
-
注意线程安全:若工厂中存在共享状态(如缓存、计数器),需添加同步锁或使用线程安全的容器;
-
Spring 环境下优先用依赖注入:无需手动创建工厂实例,通过
@Autowired
注入工厂,利用 Spring 管理生命周期。
六、总结
工厂模式的核心是 “分离创建与使用”,三种实现方式分别对应不同复杂度的场景:
-
简单工厂模式是 “入门款”,适合快速落地;
-
工厂方法模式是 “进阶款”,适合单一品类扩展;
-
抽象工厂模式是 “高级款”,适合多品类产品族。
在实际开发中,需根据产品复杂度、扩展需求选择合适的模式,同时结合框架特性(如 Spring 的依赖注入)简化实现,避免过度设计。掌握工厂模式,不仅能提升代码的可维护性,更能理解框架底层的设计思想(如 Spring、JDK 的工厂应用),为架构设计打下基础。