工厂模式是 Java 中最常用的设计模式之一,它属于创建型模式,其核心思想是将对象的创建和使用分离。

就是创建对象的地方被封装起来,客户端(调用者)不需要知道具体对象是如何创建的,只需要向工厂请求一个对象即可,这极大地降低了系统的耦合度。
为什么需要工厂模式?(解决的问题)
假设我们没有使用工厂模式,要创建一个产品对象:
// 客户端代码
public class Client {
public static void main(String[] args) {
// 客户端直接依赖于具体的产品类
ProductA product = new ProductA();
product.use();
}
}
这里存在几个问题:
- 高耦合:客户端代码
new ProductA()直接依赖于ProductA这个具体类,如果将来需要更换ProductA为ProductB,或者需要根据配置动态选择产品,就必须修改客户端代码。 - 违反开闭原则:对扩展开放,对修改关闭,如果系统需要增加一个新的产品
ProductC,我们就必须修改客户端代码,去添加new ProductC()的逻辑,而不是简单地扩展。 - 职责混乱:客户端的职责是“使用产品”,但它却承担了“创建产品”的职责。
工厂模式就是为了解决这些问题而生的。

工厂模式的几种形态
工厂模式不是单一的模式,它根据抽象程度的不同,主要有以下三种形态:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
下面我们逐一讲解。
简单工厂模式
它不属于 GoF 的 23 种经典设计模式,但因其简单易懂,在实际开发中非常常用,所以通常作为学习工厂模式的入门。
核心思想:由一个工厂类来负责创建所有产品类的实例,客户端传入一个参数,工厂根据这个参数决定创建哪种产品。
结构:
- 产品接口:定义所有产品共有的接口。
- 具体产品:实现产品接口的具体类。
- 工厂类:负责创建所有具体产品。
代码示例:
产品接口
// Product.java
public interface Product {
void use();
}
具体产品
// ProductA.java
public class ProductA implements Product {
@Override
public void use() {
System.out.println("使用产品 A");
}
}
// ProductB.java
public class ProductB implements Product {
@Override
public void use() {
System.out.println("使用产品 B");
}
}
工厂类
// SimpleFactory.java
public class SimpleFactory {
// 根据参数创建不同的产品实例
public static Product createProduct(String type) {
if ("A".equalsIgnoreCase(type)) {
return new ProductA();
} else if ("B".equalsIgnoreCase(type)) {
return new ProductB();
}
throw new IllegalArgumentException("未知的产品类型: " + type);
}
}
客户端使用
// Client.java
public class Client {
public static void main(String[] args) {
// 客户端通过工厂获取产品,而不直接 new
Product productA = SimpleFactory.createProduct("A");
productA.use(); // 输出: 使用产品 A
Product productB = SimpleFactory.createProduct("B");
productB.use(); // 输出: 使用产品 B
}
}
优点:
- 客户端代码非常简单,只需知道工厂和产品接口即可。
- 将创建逻辑集中到了一处,便于管理。
缺点:
- 工厂类职责过重,包含了所有产品的创建逻辑,一旦产品种类增多,工厂类会变得非常庞大和臃肿。
- 违反开闭原则,每当增加一个新产品时,都需要修改工厂类的
createProduct方法。
工厂方法模式
这是 GoF 正式定义的工厂模式,它解决了简单工厂模式中工厂类职责过重的问题。
核心思想:定义一个用于创建对象的接口(工厂接口),但让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
结构:
- 产品接口:定义产品的接口。
- 具体产品:实现产品接口。
- 工厂接口:定义一个创建产品的抽象方法
createProduct()。 - 具体工厂:实现工厂接口,负责创建一个具体的产品。
代码示例:
产品接口和具体产品 (与简单工厂模式相同)
// Product.java
public interface Product {
void use();
}
// ProductA.java
public class ProductA implements Product {
@Override
public void use() {
System.out.println("使用产品 A");
}
}
// ProductB.java
public class ProductB implements Product {
@Override
public void use() {
System.out.println("使用产品 B");
}
}
工厂接口和具体工厂
// Factory.java
public interface Factory {
Product createProduct();
}
// FactoryA.java
public class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ProductA(); // 具体工厂 A 创建具体产品 A
}
}
// FactoryB.java
public class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ProductB(); // 具体工厂 B 创建具体产品 B
}
}
客户端使用
// Client.java
public class Client {
public static void main(String[] args) {
// 客户端需要知道并选择具体的工厂
Factory factoryA = new FactoryA();
Product productA = factoryA.createProduct();
productA.use(); // 输出: 使用产品 A
Factory factoryB = new FactoryB();
Product productB = factoryB.createProduct();
productB.use(); // 输出: 使用产品 B
}
}
优点:
- 符合开闭原则:增加新产品时,只需增加一个具体产品类和一个对应的具体工厂类,而无需修改已有的工厂代码。
- 职责清晰:每个具体工厂只负责创建一个具体的产品,符合单一职责原则。
缺点:
- 类的数量增多:每增加一个产品,就需要增加一个对应的工厂类,这会导致系统中类的数量成对增加,增加了系统的复杂度。
抽象工厂模式
当产品族出现时,工厂方法模式就显得力不从心了,抽象工厂模式应运而生。
核心思想:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类,它强调的是一系列相关产品的创建。
结构:
- 产品族接口:
ProductA和ProductB可以是一个产品族(都属于品牌1),ProductA2和ProductB2可以是另一个产品族(都属于品牌2)。 - 具体产品:实现产品族接口。
- 抽象工厂接口:定义创建整个产品族的方法,如
createProductA()和createProductB()。 - 具体工厂:实现抽象工厂接口,负责创建一个完整的产品族。
代码示例:
假设我们有两个产品族:Product1 和 Product2,每个产品族都有 A 和 B 两种产品。
产品族接口
// 产品族 1
public interface ProductA1 {
void use();
}
// 产品族 2
public interface ProductA2 {
void use();
}
// 产品族 1
public interface ProductB1 {
void use();
}
// 产品族 2
public interface ProductB2 {
void use();
}
具体产品
// 品牌1 的产品
public class ConcreteProductA1 implements ProductA1 {
@Override
public void use() {
System.out.println("使用品牌1的产品 A1");
}
}
public class ConcreteProductB1 implements ProductB1 {
@Override
public void use() {
System.out.println("使用品牌1的产品 B1");
}
}
// 品牌2 的产品
public class ConcreteProductA2 implements ProductA2 {
@Override
public void use() {
System.out.println("使用品牌2的产品 A2");
}
}
public class ConcreteProductB2 implements ProductB2 {
@Override
public void use() {
System.out.println("使用品牌2的产品 B2");
}
}
抽象工厂接口
public interface AbstractFactory {
// 创建产品族 A
ProductA1 createProductA1();
// 创建产品族 B
ProductB1 createProductB1();
}
具体工厂
// 品牌1 的工厂
public class Factory1 implements AbstractFactory {
@Override
public ProductA1 createProductA1() {
return new ConcreteProductA1();
}
@Override
public ProductB1 createProductB1() {
return new ConcreteProductB1();
}
}
// 品牌2 的工厂
public class Factory2 implements AbstractFactory {
@Override
public ProductA1 createProductA1() {
return new ConcreteProductA2(); // 注意:这里返回的是产品族2的产品,这只是为了演示,通常接口名会不同
}
@Override
public ProductB1 createProductB1() {
return new ConcreteProductB2(); // 同上
}
}
(注意:为了演示,上面的接口设计有点特殊,更常见的是 AbstractFactory 会定义 createA() 和 createB(),然后每个工厂实现它们来创建自己品牌下的A和B产品。)
客户端使用
// Client.java
public class Client {
public static void main(String[] args) {
// 客户端使用抽象工厂
AbstractFactory factory1 = new Factory1();
ProductA1 a1 = factory1.createProductA1();
ProductB1 b1 = factory1.createProductB1();
a1.use(); // 输出: 使用品牌1的产品 A1
b1.use(); // 输出: 使用品牌1的产品 B1
AbstractFactory factory2 = new Factory2();
ProductA1 a2 = factory2.createProductA1();
ProductB1 b2 = factory2.createProductB1();
a2.use(); // 输出: 使用品牌2的产品 A2
b2.use(); // 输出: 使用品牌2的产品 B2
}
}
优点:
- 保证产品族的一致性:客户端一次性获取的是一整个产品族,保证了产品之间的兼容性。
- 符合开闭原则:增加新的产品族时,只需增加一个新的具体工厂即可。
缺点:
- 扩展产品非常困难:如果需要在产品族中增加一个新产品(
ProductC),那么所有的具体工厂都需要进行修改,这违反了开闭原则,抽象工厂模式易于扩展产品族,但难于扩展新产品。
总结与对比
| 特性 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
|---|---|---|---|
| 创建对象 | 一个工厂类创建所有产品 | 每个产品对应一个工厂 | 一个工厂创建一个产品族 |
| 核心 | if-else 或 switch 判断 |
抽象工厂接口,子类实现 | 抽象工厂接口,创建多个产品 |
| 优点 | 客户端代码简单,创建集中 | 符合开闭原则,扩展产品方便 | 保证产品族一致性,扩展产品族方便 |
| 缺点 | 工厂类臃肿,违反开闭原则 | 类的数量增多,扩展产品族困难 | 扩展新产品困难,违反开闭原则 |
| 适用场景 | 产品种类少,创建逻辑简单 | 产品种类多,但产品族单一 | 需要创建多个相互关联的产品对象 |
何时使用?
- 使用简单工厂模式:当你系统中的产品种类非常有限,并且未来不太可能频繁增加时,它是一个快速实现的好方法。
- 使用工厂方法模式:当你系统中有多种产品,并且你希望在未来可以轻松地增加新的产品,而不影响现有代码时,这是最常用、最经典的工厂模式。
- 使用抽象工厂模式:当你需要创建的对象是一个产品族,并且你希望确保客户端使用的是同一品牌下的、相互兼容的产品时,UI 皮肤(Windows 风格、macOS 风格),或者数据库连接(MySQL 连接池、Oracle 连接池)。
Java 中的实际应用
工厂模式在 Java 标准库和框架中无处不在:
java.util.Calendar的getInstance()方法:这是一个典型的简单工厂或静态工厂方法,你不需要关心具体是GregorianCalendar还是其他Calendar的子类,调用getInstance()就会返回一个合适的实例。java.text.NumberFormat的getInstance()方法:同样,根据你传入的Locale(区域设置),它会返回一个适合该区域的数字格式化器。java.net.URL的openConnection()方法:它根据 URL 的协议(http,https,ftp等)返回一个合适的URLConnection对象。- Spring 框架:Spring 的核心是 IoC(控制反转)容器,它就是一个巨大的工厂,你通过
@Autowired或getBean()方法向 Spring 工厂请求一个 Bean(对象),而无需关心这个 Bean 是如何被创建和配置的。
希望这个详细的解释能帮助你彻底理解 Java 中的工厂模式!
