Java 设计模式之工厂模式:对象创建的艺术(含 UML 图解)
在软件开发中,对象的创建是最基础也最频繁的操作。如果直接在代码中到处使用new关键字创建对象,会导致代码耦合度高、扩展性差。工厂模式(Factory Pattern)通过将对象创建的逻辑封装在专门的工厂类中,实现了 "创建与使用分离",是解决对象创建问题的经典方案。
工厂模式并非单一模式,而是一个模式家族,包含简单工厂模式、工厂方法模式和抽象工厂模式三种形式。本文将详细讲解这三种模式的实现与应用场景。
一、工厂模式的核心思想
工厂模式属于创建型设计模式,它的核心思想是:将对象的创建过程封装起来,让客户端无需关心对象的具体创建细节,只需通过工厂获取所需对象。
生活中的工厂模式例子随处可见:
- 汽车工厂生产不同品牌的汽车,消费者无需知道汽车的生产细节,直接从工厂提车即可
- 披萨店(工厂)制作不同口味的披萨,顾客只需点单(调用工厂方法),无需自己动手制作
工厂模式的核心价值在于:
- 降低耦合:客户端与具体产品解耦,只依赖工厂和抽象产品
- 提高扩展性:新增产品时只需扩展工厂,无需修改客户端
- 统一管理:对象创建逻辑集中在工厂,便于维护和控制
二、简单工厂模式(Simple Factory)
简单工厂模式是工厂模式中最基础的形式,它通过一个工厂类根据传入的参数决定创建哪种产品对象。
核心角色
- 抽象产品(Product):定义产品的公共接口
- 具体产品(Concrete Product):实现抽象产品接口,是工厂创建的目标
- 工厂(Factory):根据参数创建具体产品的实例
UML 类图
+------------------+
| Product |
+------------------+
| + operation(): void |
+------------------+
^
|
+------------------+ +------------------+
| ConcreteProductA | | ConcreteProductB |
+------------------+ +------------------+
| + operation(): void | | + operation(): void |
+------------------+ +------------------+
^ ^
| |
+------------------+
| Factory |
+------------------+
| + createProduct(type): Product |
+------------------+
代码实现:计算器工厂
以简单计算器为例,实现一个能创建加法、减法运算器的简单工厂:
1. 抽象产品(运算接口)
/**
* 抽象产品:运算接口
*/
public interface Operation {
double calculate(double a, double b);
}
2. 具体产品(加法、减法实现)
/**
* 具体产品:加法运算
*/
public class AddOperation implements Operation {
@Override
public double calculate(double a, double b) {
return a + b;
}
}
/**
* 具体产品:减法运算
*/
public class SubtractOperation implements Operation {
@Override
public double calculate(double a, double b) {
return a - b;
}
}
3. 工厂类(运算器工厂)
/**
* 简单工厂:运算器工厂
*/
public class OperationFactory {
/**
* 根据类型创建运算器
* @param type 运算类型:"+" "-"
* @return 具体运算器实例
*/
public static Operation createOperation(String type) {
Operation operation = null;
switch (type) {
case "+":
operation = new AddOperation();
break;
case "-":
operation = new SubtractOperation();
break;
default:
throw new IllegalArgumentException("不支持的运算类型");
}
return operation;
}
}
4. 客户端使用
public class Client {
public static void main(String[] args) {
// 从工厂获取加法运算器
Operation addOp = OperationFactory.createOperation("+");
System.out.println("3 + 5 = " + addOp.calculate(3, 5)); // 8.0
// 从工厂获取减法运算器
Operation subOp = OperationFactory.createOperation("-");
System.out.println("10 - 4 = " + subOp.calculate(10, 4)); // 6.0
}
}
简单工厂模式的优缺点
- 优点:实现简单,客户端无需知道产品创建细节
- 缺点:违反开闭原则,新增产品需修改工厂类的判断逻辑,工厂类职责过重
三、工厂方法模式(Factory Method)
为了解决简单工厂模式的缺陷,工厂方法模式将工厂抽象化,为每个产品创建对应的工厂类,使新增产品时只需扩展工厂,无需修改原有代码。
核心角色
- 抽象产品(Product):定义产品的公共接口
- 具体产品(Concrete Product):实现抽象产品接口
- 抽象工厂(Abstract Factory):定义创建产品的接口
- 具体工厂(Concrete Factory):实现抽象工厂接口,创建具体产品
UML 类图
+------------------+ +------------------+
| Product | | AbstractFactory |
+------------------+ +------------------+
| + operation(): void | | + createProduct(): Product |
+------------------+ +------------------+
^ ^
| |
+------------------+ +------------------+
| ConcreteProductA |<----->| FactoryA |
+------------------+ +------------------+
| + operation(): void | | + createProduct(): Product |
+------------------+ +------------------+
+------------------+ +------------------+
| ConcreteProductB |<----->| FactoryB |
+------------------+ +------------------+
| + operation(): void | | + createProduct(): Product |
+------------------+ +------------------+
代码实现:日志记录器工厂
以日志记录为例,实现文件日志和数据库日志的工厂方法模式:
1. 抽象产品(日志接口)
/**
* 抽象产品:日志记录器
*/
public interface Logger {
void log(String message);
}
2. 具体产品(文件日志、数据库日志)
/**
* 具体产品:文件日志记录器
*/
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("文件日志:" + message);
}
}
/**
* 具体产品:数据库日志记录器
*/
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
System.out.println("数据库日志:" + message);
}
}
3. 抽象工厂(日志工厂接口)
/**
* 抽象工厂:日志工厂
*/
public interface LoggerFactory {
Logger createLogger();
}
4. 具体工厂(文件日志工厂、数据库日志工厂)
/**
* 具体工厂:文件日志工厂
*/
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
// 可以在这里添加文件日志的初始化逻辑
return new FileLogger();
}
}
/**
* 具体工厂:数据库日志工厂
*/
public class DatabaseLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
// 可以在这里添加数据库连接等初始化逻辑
return new DatabaseLogger();
}
}
5. 客户端使用
public class Client {
public static void main(String[] args) {
// 创建文件日志工厂和日志记录器
LoggerFactory fileFactory = new FileLoggerFactory();
Logger fileLogger = fileFactory.createLogger();
fileLogger.log("系统启动成功"); // 文件日志:系统启动成功
// 创建数据库日志工厂和日志记录器
LoggerFactory dbFactory = new DatabaseLoggerFactory();
Logger dbLogger = dbFactory.createLogger();
dbLogger.log("用户登录"); // 数据库日志:用户登录
}
}
工厂方法模式的优缺点
- 优点:符合开闭原则,新增产品只需新增对应的产品类和工厂类;单一职责,每个工厂只负责创建一种产品
- 缺点:类数量增多,系统复杂度提高;客户端需要知道具体工厂才能获取对应产品
四、抽象工厂模式(Abstract Factory)
当需要创建一系列相互关联或依赖的产品时,抽象工厂模式应运而生。它提供一个接口,用于创建多个产品族中的产品对象。
核心概念
- 产品族:同一工厂生产的、功能相关联的一组产品(如华为工厂生产的手机、平板)
- 产品等级结构:同一类产品的不同实现(如手机有华为手机、苹果手机)
核心角色
- 抽象产品(Abstract Product):多个产品等级结构的接口
- 具体产品(Concrete Product):实现抽象产品接口
- 抽象工厂(Abstract Factory):声明创建多个产品族的方法
- 具体工厂(Concrete Factory):实现抽象工厂,创建同一产品族的产品
UML 类图
+------------------+ +------------------+
| Phone | | Tablet |
+------------------+ +------------------+
| + call(): void | | + browse(): void |
+------------------+ +------------------+
^ ^
| |
+------------------+ +------------------+
| HuaweiPhone | | HuaweiTablet |
+------------------+ +------------------+
| + call(): void | | + browse(): void |
+------------------+ +------------------+
+------------------+ +------------------+
| IPhone | | IPad |
+------------------+ +------------------+
| + call(): void | | + browse(): void |
+------------------+ +------------------+
^ ^
| |
+------------------+
| AbstractFactory |
+------------------+
| + createPhone(): Phone |
| + createTablet(): Tablet |
+------------------+
^
|
+------------------+ +------------------+
| HuaweiFactory | | AppleFactory |
+------------------+ +------------------+
| + createPhone(): Phone | | + createPhone(): Phone |
| + createTablet(): Tablet | | + createTablet(): Tablet |
+------------------+ +------------------+
代码实现:电子设备工厂
以华为和苹果的产品族为例,实现能生产手机和平板的抽象工厂:
1. 抽象产品(手机、平板接口)
/**
* 抽象产品:手机
*/
public interface Phone {
void call();
}
/**
* 抽象产品:平板
*/
public interface Tablet {
void browse();
}
2. 具体产品(华为 / 苹果的手机和平板)
/**
* 具体产品:华为手机
*/
public class HuaweiPhone implements Phone {
@Override
public void call() {
System.out.println("用华为手机打电话");
}
}
/**
* 具体产品:华为平板
*/
public class HuaweiTablet implements Tablet {
@Override
public void browse() {
System.out.println("用华为平板浏览网页");
}
}
/**
* 具体产品:苹果手机
*/
public class IPhone implements Phone {
@Override
public void call() {
System.out.println("用iPhone打电话");
}
}
/**
* 具体产品:苹果平板
*/
public class IPad implements Tablet {
@Override
public void browse() {
System.out.println("用iPad浏览网页");
}
}
3. 抽象工厂(电子设备工厂接口)
/**
* 抽象工厂:电子设备工厂
*/
public interface ElectronicFactory {
Phone createPhone(); // 生产手机
Tablet createTablet();// 生产平板
}
4. 具体工厂(华为工厂、苹果工厂)
/**
* 具体工厂:华为工厂
*/
public class HuaweiFactory implements ElectronicFactory {
@Override
public Phone createPhone() {
return new HuaweiPhone();
}
@Override
public Tablet createTablet() {
return new HuaweiTablet();
}
}
/**
* 具体工厂:苹果工厂
*/
public class AppleFactory implements ElectronicFactory {
@Override
public Phone createPhone() {
return new IPhone();
}
@Override
public Tablet createTablet() {
return new IPad();
}
}
5. 客户端使用
public class Client {
public static void main(String[] args) {
// 使用华为工厂生产华为产品族
ElectronicFactory huaweiFactory = new HuaweiFactory();
Phone huaweiPhone = huaweiFactory.createPhone();
Tablet huaweiTablet = huaweiFactory.createTablet();
huaweiPhone.call(); // 用华为手机打电话
huaweiTablet.browse(); // 用华为平板浏览网页
// 使用苹果工厂生产苹果产品族
ElectronicFactory appleFactory = new AppleFactory();
Phone iPhone = appleFactory.createPhone();
Tablet iPad = appleFactory.createTablet();
iPhone.call(); // 用iPhone打电话
iPad.browse(); // 用iPad浏览网页
}
}
抽象工厂模式的优缺点
- 优点:便于管理同一产品族的产品;保证产品族内产品的兼容性;符合开闭原则(扩展产品族只需新增工厂)
- 缺点:扩展新的产品等级结构困难(如新增手表产品,需修改所有工厂接口和实现)
五、三种工厂模式的对比与选择
| 模式 | 核心特点 | 适用场景 | 优缺点对比 |
|---|---|---|---|
| 简单工厂 | 一个工厂类创建所有产品 | 产品种类少,变化不频繁 | 实现简单但违反开闭原则 |
| 工厂方法 | 一个工厂对应一个产品 | 产品种类较多,需要灵活扩展 | 符合开闭原则但类数量增多 |
| 抽象工厂 | 一个工厂创建一个产品族 | 需要创建相互关联的产品组,强调产品族兼容性 | 适合产品族扩展,但难扩展新产品 |
选择建议:
- 简单场景用简单工厂,快速实现
- 单一产品扩展用工厂方法,兼顾灵活与简单
- 多产品族场景用抽象工厂,保证产品关联性
六、工厂模式在 Java 中的应用
- JDK 中的应用:
java.util.Calendar:通过getInstance()方法(简单工厂)创建日历实例java.sql.DriverManager:通过getConnection()获取不同数据库连接(工厂方法)java.net.URLStreamHandlerFactory:创建不同协议的流处理器(抽象工厂)
- 框架中的应用:
- Spring 的
BeanFactory:通过工厂模式创建和管理 Bean 对象 - MyBatis 的
SqlSessionFactory:创建SqlSession对象(工厂方法)
- Spring 的
总结
工厂模式通过封装对象创建逻辑,完美诠释了 "创建与使用分离" 的设计思想。从简单工厂到抽象工厂,模式的复杂度逐渐增加,但灵活性和适用性也随之提升。
在实际开发中,不必盲目追求复杂模式,应根据业务场景选择最合适的实现:简单场景用简单工厂提高开发效率,复杂场景用工厂方法或抽象工厂保证扩展性。记住,设计模式的核心是解决问题,而非炫技。
掌握工厂模式,不仅能写出更优雅的代码,更能培养 "封装变化" 的设计思维 —— 这正是优秀程序员与普通程序员的重要区别。