工厂方法模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但将具体创建哪个类的对象的决定权推迟到子类。其核心在于让子类来决定实例化哪一个类,从而将对象的创建与使用解耦,提高了系统的灵活性和可扩展性。
核心结构与角色
工厂方法模式主要包含以下角色,其协作关系如下图所示(以抽象概念表示):
| 角色 | 职责 | 说明 |
|---|---|---|
| 抽象产品 (Product) | 定义产品的接口 | 所有具体产品必须实现的公共接口或抽象类。 |
| 具体产品 (Concrete Product) | 实现抽象产品接口 | 工厂方法模式创建的具体对象类。 |
| 抽象工厂/创建者 (Creator) | 声明工厂方法 | 返回一个抽象产品类型的对象。它可以包含一些依赖于产品的核心业务逻辑。 |
| 具体工厂/具体创建者 (Concrete Creator) | 实现工厂方法 | 重写工厂方法,返回一个具体产品的实例。客户端通常与具体工厂交互。 |
实现原理与步骤
工厂方法模式的核心是利用多态性,将对象创建的责任从客户端代码转移到专门的工厂类中。客户端代码仅依赖于抽象产品接口和抽象工厂,无需关心具体产品的类名和创建细节。
其实现步骤如下:
- 定义抽象产品接口:声明所有产品对象共有的方法。
- 创建具体产品类:实现抽象产品接口,定义具体的产品对象。
- 定义抽象工厂类:声明一个返回抽象产品对象的工厂方法。该类可以包含一些不依赖于具体产品类型的核心业务逻辑。
- 创建具体工厂类:继承抽象工厂类,并重写工厂方法,使其返回一个具体产品的实例。
代码示例:模拟水果加工厂
以下是一个完整案例,模拟一个水果加工厂生产不同水果罐头的过程。
步骤1:定义抽象产品 (Fruit)
java
// 抽象产品:水果
public interface Fruit {
void produce(); // 生产水果罐头
}
步骤2:创建具体产品类 (Apple, Orange)
java
// 具体产品:苹果罐头
public class Apple implements Fruit {
@Override
public void produce() {
System.out.println("生产了一罐苹果罐头。");
}
}
// 具体产品:橙子罐头
public class Orange implements Fruit {
@Override
public void produce() {
System.out.println("生产了一罐橙子罐头。");
}
}
步骤3:定义抽象工厂 (FruitFactory)
java
// 抽象工厂/创建者
public abstract class FruitFactory {
// 工厂方法:由子类实现,用于创建具体产品
public abstract Fruit createFruit();
// 可以包含一些不依赖于具体产品的业务逻辑
public void processFruit() {
Fruit fruit = createFruit(); // 调用工厂方法创建产品
System.out.println("开始加工水果...");
fruit.produce(); // 使用产品
System.out.println("水果加工完成,准备包装。");
}
}
步骤4:创建具体工厂类 (AppleFactory, OrangeFactory)
java
// 具体工厂:苹果工厂
public class AppleFactory extends FruitFactory {
@Override
public Fruit createFruit() {
return new Apple(); // 工厂方法返回具体产品:苹果
}
}
// 具体工厂:橙子工厂
public class OrangeFactory extends FruitFactory {
@Override
public Fruit createFruit() {
return new Orange(); // 工厂方法返回具体产品:橙子
}
}
步骤5:客户端使用
java
public class Client {
public static void main(String[] args) {
// 客户端代码依赖于抽象工厂和抽象产品,而非具体类
FruitFactory appleFactory = new AppleFactory();
appleFactory.processFruit(); // 输出:开始加工水果... 生产了一罐苹果罐头。 水果加工完成,准备包装。
FruitFactory orangeFactory = new OrangeFactory();
orangeFactory.processFruit(); // 输出:开始加工水果... 生产了一罐橙子罐头。 水果加工完成,准备包装。
// 如果需要新增产品,例如香蕉
// 1. 新增具体产品类 Banana implements Fruit
// 2. 新增具体工厂类 BananaFactory extends FruitFactory
// 3. 客户端使用:FruitFactory bananaFactory = new BananaFactory();
// 原有代码(抽象工厂、其他具体工厂、客户端调用逻辑)完全不需要修改,符合"开闭原则"。
}
}
模式对比:工厂方法 vs. 简单工厂
工厂方法模式常与简单工厂模式(静态工厂方法)对比。简单工厂将所有创建逻辑集中在一个工厂类中,通过传入参数决定创建何种产品。
java
// 简单工厂示例
public class SimpleFruitFactory {
public static Fruit createFruit(String type) {
if ("apple".equalsIgnoreCase(type)) {
return new Apple();
} else if ("orange".equalsIgnoreCase(type)) {
return new Orange();
}
throw new IllegalArgumentException("Unknown fruit type");
}
}
// 使用:Fruit apple = SimpleFruitFactory.createFruit("apple");
对比分析:
- 简单工厂 :优点是将创建逻辑封装,客户端无需知道具体类名。缺点是违反了开闭原则 ,当需要新增产品时,必须修改工厂类的
createFruit方法(增加新的if-else分支)。 - 工厂方法 :将创建逻辑分散到各个具体工厂中。新增产品时,只需增加新的具体产品类和对应的具体工厂类,无需修改任何现有工厂代码,完全符合开闭原则,系统扩展性更强。
应用场景与优势
工厂方法模式适用于以下场景:
- 无法预知对象确切类型:当编写代码时,无法预知将要创建的对象的具体类,需要由运行时条件决定。
- 希望将产品创建与使用解耦 :避免在业务代码中直接使用
new关键字创建具体对象,降低模块间的耦合度。 - 系统需要良好的扩展性:预计未来会频繁新增产品种类。
- 复用对象创建过程:当对象的创建过程复杂(如需要配置、依赖其他服务)时,可以在工厂方法中封装这些过程,便于复用。
优势包括:
- 符合开闭原则:新增产品类型时,只需扩展新的具体工厂和产品类,无需修改客户端和现有工厂代码。
- 单一职责原则:将产品创建代码集中在工厂类中,使得代码更易维护。
- 解耦 :客户端代码仅依赖于抽象接口(
Fruit和FruitFactory),与具体实现分离,提高了代码的灵活性和可测试性。
缺点:
- 类的数量增加:每增加一个产品,通常就需要增加一个具体产品类和一个具体工厂类,可能导致系统类数量膨胀,增加复杂度。
在Java生态中的应用实例
- Java集合框架 :
java.util.Collection接口的iterator()方法就是一个工厂方法。ArrayList和HashSet等具体集合类重写此方法,分别返回Itr和KeyIterator等不同的具体迭代器产品。 - Spring Framework :
BeanFactory和ApplicationContext是核心容器,它们使用工厂方法模式来创建和管理Bean对象。例如,ApplicationContext的getBean()方法根据配置信息(如XML、注解)动态创建并返回所需的对象实例。 - 日志框架 :如SLF4J的
LoggerFactory.getLogger()方法。根据底层绑定的具体日志实现(Logback、Log4j2),该方法返回对应的Logger产品实例。
总结
工厂方法模式通过引入"工厂"这一抽象层 ,将对象的实例化过程封装起来,使系统在不修改现有代码的前提下,能够应对变化和扩展。它是实现依赖倒置原则 和开闭原则的经典范例。在选择使用时,如果产品结构相对稳定,新增不频繁,简单工厂可能更简洁;如果系统需要高度可扩展,产品种类可能频繁增加,工厂方法模式是更优的选择。在实际开发中,结合Spring等IoC容器,可以更加优雅地实现和管理工厂方法模式。