设计模式精讲系列:什么是工厂模式?工厂模式有哪些类型?如何使用它们?

你好,我是猿java

工厂设计模式是一种创建对象的设计模式,它的主要目的是通过定义一个接口来创建对象,使得子类决定实例化哪个类。这篇文章,我们将分析工厂模式是什么,它包含什么类型以及如何工作。

从整体上看,工厂模式可以分为三种主要类型:简单工厂模式、工厂方法模式和抽象工厂模式。

img

1. 简单工厂模式

1.1 概述

简单工厂模式并不是一个正式的设计模式,而是一个创建对象的简单方法。在简单工厂模式中,通常会有一个工厂类,它根据参数的不同返回不同类型的对象。这个模式的优点是简单明了,但缺点是违背了开闭原则。

1.2 角色

  • 工厂类:负责创建产品的实例,提供一个静态方法供外部调用。
  • 产品类:所有产品类都需实现相同的接口,用于定义产品的公共行为。
  • 客户端:通过工厂类来获取产品实例,并使用这些实例。

1.3 实现

下面我们通过一个简单的示例代码来展示是简单工厂模式如何实现:

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 产品接口
interface Product {
void use();
}

// 具体产品A
class ConcreteProduct1 implements Product {
public void use() {
System.out.println("产品1");
}
}

// 具体产品B
class ConcreteProduct2 implements Product {
public void use() {
System.out.println("产品2");
}
}

// 简单工厂类,此处可以根据类型返回对应的对象,但缺点是违背了开闭原则
class SimpleFactory {
public static Product createProduct(String type) {
switch (type) {
case "1":
return new ConcreteProduct1();
case "2":
return new ConcreteProduct2();
default:
return null;
}
}
}

// 客户端
public class Client {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("1");
productA.use();

Product productB = SimpleFactory.createProduct("2");
productB.use();
}
}

代码分析:

  • 产品接口 (Product) : 所有产品类必须实现这个接口,定义产品的公共行为(这里是 use() 方法)。
  • 具体产品 (ConcreteProduct1, ConcreteProduct2) : 实现了 Product 接口,提供实际的产品功能。
  • 工厂类 (SimpleFactory) : 通过 createProduct 方法,根据传入的类型参数返回具体的产品实例。这个方法硬编码了对产品类型的判断,这直接使得 SimpleFactory 依赖于具体的产品类。
  • 客户端 (Client) : 用户使用 SimpleFactory 来创造产品,并调用其方法。客户端只需了解产品接口而不需要关心产品的具体实现。

在上述示例代码中,SimpleFactory类是一个简单工厂类,它可以根据类型返回相应的对象,这是在日常开发中很多程序员容易编写的代码,但是,简单工厂类违背了开闭原则。

1.4 优缺点

优点

  • 简单明了,易于理解和实现。
  • 适合产品较少、变化不大的场景。

缺点

  • 一旦需要增加新的产品,工厂类就必须修改,不符合开闭原则。
  • 工厂类的职责过于集中,增加了其复杂性。

2. 工厂方法模式

2.1 概述

工厂方法模式是一种定义一个创建对象的接口,但由子类来决定要实例化的类,通过这种方式,工厂方法模式避免了简单工厂模式所带来的扩展问题,并遵循了开闭原则。

2.2 角色

  • 抽象工厂(Creator):声明工厂方法,返回一个产品。
  • 具体工厂(Concrete Creator):实现工厂方法,返回具体产品的实例。
  • 抽象产品(Product):定义产品的公共接口。
  • 具体产品(Concrete Product):实现抽象产品的具体类。

2.3 实现

下面我们通过一个示例代码来展示工厂方法模式的实现:
img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 抽象产品
interface Product {
void use();
}

// 具体产品A
class ConcreteProductA implements Product {
public void use() {
System.out.println("使用产品A");
}
}

// 具体产品B
class ConcreteProductB implements Product {
public void use() {
System.out.println("使用产品B");
}
}

// 抽象工厂
abstract class Creator {
public abstract Product factoryMethod();
}

// 具体工厂A
class ConcreteCreatorA extends Creator {
public Product factoryMethod() {
return new ConcreteProductA();
}
}

// 具体工厂B
class ConcreteCreatorB extends Creator {
public Product factoryMethod() {
return new ConcreteProductB();
}
}

// 客户端
public class Client {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
Product productA = creatorA.factoryMethod();
productA.use();

Creator creatorB = new ConcreteCreatorB();
Product productB = creatorB.factoryMethod();
productB.use();
}
}

代码分析:

  • 抽象产品 (Product) : 与简单工厂模式类似,定义了产品的公共接口。
  • 具体产品 (ConcreteProductA, ConcreteProductB) : 实现 Product 接口,提供具体的产品实现。
  • 抽象工厂 (Creator) : 定义了一个工厂方法 factoryMethod(),这个方法将由具体工厂实现,以返回具体产品。
  • 具体工厂 (ConcreteCreatorA, ConcreteCreatorB) : 继承自抽象工厂,实现 factoryMethod(),返回相应的具体产品实例。
  • 客户端 (Client) : 通过具体工厂类创建产品,从而减少了与产品创建过程的耦合。

工厂方法模式的设计使得增加新产品时,只需新增相应的具体工厂类,符合开闭原则,增强了代码的可维护性。

2.4 优缺点

优点

  • 遵循开闭原则,可以很方便地扩展新的产品。
  • 每个具体工厂只需关心自己创建的产品,减少了耦合。

缺点

  • 需要创建多个具体工厂,增加了系统复杂性。
  • 客户端需要了解具体工厂的参数,不够灵活。

3. 抽象工厂模式

3.1 概述

抽象工厂模式是为了解决工厂方法模式所无法处理的多个产品族的问题。抽象工厂提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。通过抽象工厂模式,可以更方便地创建多个产品族的对象。

3.2 角色

  • 抽象工厂(Abstract Factory):声明创建抽象产品的接口。
  • 具体工厂(Concrete Factory):实现抽象工厂的接口,创建具体产品。
  • 抽象产品(Abstract Product):声明具体相关产品的接口。
  • 具体产品(Concrete Product):实现抽象产品的具体类。

3.3 实现

下面我们通过一个简单的示例代码来展示是抽象工厂模式如何实现:

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 抽象产品A
interface ProductA {
void use();
}

// 抽象产品B
interface ProductB {
void use();
}

// 具体产品A1
class ProductA1 implements ProductA {
public void use() {
System.out.println("使用产品A1");
}
}

// 具体产品A2
class ProductA2 implements ProductA {
public void use() {
System.out.println("使用产品A2");
}
}

// 具体产品B1
class ProductB1 implements ProductB {
public void use() {
System.out.println("使用产品B1");
}
}

// 具体产品B2
class ProductB2 implements ProductB {
public void use() {
System.out.println("使用产品B2");
}
}

// 抽象工厂
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}

// 具体工厂1
class ConcreteFactory1 implements AbstractFactory {
public ProductA createProductA() {
return new ProductA1();
}

public ProductB createProductB() {
return new ProductB1();
}
}

// 具体工厂2
class ConcreteFactory2 implements AbstractFactory {
public ProductA createProductA() {
return new ProductA2();
}

public ProductB createProductB() {
return new ProductB2();
}
}

// 客户端
public class Client {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
productA1.use();
productB1.use();

AbstractFactory factory2 = new ConcreteFactory2();
ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA2.use();
productB2.use();
}
}

代码分析:

  • 抽象产品 (ProductA, ProductB) : 定义了两类产品的公共接口,可以分别实现不同的具体产品。
  • 具体产品 (ProductA1, ProductA2, ProductB1, ProductB2) : 实现各自的接口,表示具体的产品。
  • 抽象工厂 (AbstractFactory) : 定义创建产品 A 和产品 B 的方法。不同的具体工厂会实现这些方法,返回相应的产品。
  • 具体工厂 (ConcreteFactory1, ConcreteFactory2) : 实现抽象工厂的方法,生产具体的产品。比如 ConcreteFactory1 生产 ConcreteProductA1 和 ConcreteProductB1。
  • 客户端 (Client) : 客户端通过抽象工厂来创建产品,而不直接依赖于具体的产品类,从而实现了与具体产品的解耦。

抽象工厂模式适合于需要创建多种产品家族的场合,客户端可以通过改变工厂来实现不同产品组的创建,减少了对具体产品类的依赖。引入抽象工厂模式可以减少耦合,提升系统的灵活性。

3.4 优缺点

优点

  • 可以创建相关或相互依赖的对象,减少耦合。
  • 遵循开闭原则,增加新的产品族时,无需修改已有代码。

缺点

  • 增加了系统的复杂性。需要有一个完整的产品族。
  • 随着产品的增加,工厂类会变得臃肿。

4. 三者对比

在分析完三者之上,我们对简单工厂模式、工厂方法模式和抽象工厂模式进行一个简单的对比:

4.1. 简单工厂模式

定义:简单工厂模式并不是一个正式的设计模式,而是一种简单的创建对象的方法。它通过一个工厂类来创建不同类型的对象。

特点

  • 客户端通过工厂类请求对象。
  • 工厂类根据参数的不同返回不同的对象。
  • 工厂类通常是静态的,不需要实现接口。

适用场景:适合于对象创建逻辑简单且数量不多的场景。

4.2. 工厂方法模式

定义:工厂方法模式是一种定义一个用于创建对象的接口,但让子类决定实例化哪一个类的模式。

特点

  • 有一个抽象的工厂接口和多个具体的工厂类实现这个接口。
  • 每个具体工厂负责创建特定类型的对象。
  • 通过多态的方式,客户端代码只依赖于抽象工厂。

适用场景:适合于需要通过子类扩展产品时,比如需要创建的对象比较复杂,或者对象种类很多时。

4.3 抽象工厂模式

定义:抽象工厂模式提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。

特点

  • 有一个抽象工厂和多个具体工厂(每个工厂可以创建多个产品)。
  • 产品通常是分为多个等级结构,如产品A、产品B。
  • 客户端依赖于抽象工厂和抽象产品。

适用场景:适合于产品族(如 UI 工具包、不同产品线)比较复杂且需要在不同平台之间切换的情况。

总结

  • 简单工厂模式:一个工厂类,简单快捷,但不够灵活。
  • 工厂方法模式:扩展性好,遵循开闭原则,可以添加新的产品。
  • 抽象工厂模式:适用于产品族的创建和管理,所有相关产品一起创建,适合复杂系统。

5. 使用工厂模式的框架

工厂模式在 Java领域有着广泛的使用,这里列举了几个常见框架:

5.1 Spring Framework

Spring框架是一个非常典型的使用工厂模式的例子。Spring使用工厂模式来创建和管理对象。以下是几个具体的实现:

  • BeanFactory:Spring的核心工厂接口,用于管理和创建Beans。BeanFactory接口提供了获取Bean的统一方法。

  • ApplicationContextApplicationContextBeanFactory的一个子接口,提供了更加丰富的功能,如国际化、事件传播等。它的实现类(例如ClassPathXmlApplicationContextAnnotationConfigApplicationContext)充当特定的工厂,实现了不同的获取Bean方式。

5.2 Hibernate

Hibernate是一个广泛使用的ORM框架,它在配置和创建SessionFactory时使用了工厂模式。

  • SessionFactory:Hibernate通过SessionFactory接口的实现类(如Configuration类)来建立与数据库的连接。开发者可以通过工厂方法获取Session对象,通过Session与数据库进行交互。

5.3 JPA

JPA (Java Persistence API)是Java的持久化标准,许多JPA实现(例如Hibernate, EclipseLink)也利用工厂模式来创建实体管理器(EntityManager)。

  • EntityManagerFactory:通过EntityManagerFactory的实现,应用程序可以创建EntityManager对象,从而与数据库进行操作。这种设计使得具体的实现可以更换而不影响客户端代码。

5.4 Apache

在 Apache的 Apache Commons 和 Apache POI也使用了工厂模式:

  • Apache Commons:在Apache Commons库中,有许多获取对象的静态工厂方法,特别是在创建工具类时。

  • Apache POI:在处理Excel文件时,它使用工厂模式来创建不同类型的Workbook对象(例如,HSSFWorkbookXSSFWorkbook),具体取决于文件格式。

6. 小结

本文,我们详细地分析工厂模式以及使用示例代码进行实现,工厂设计模式提供了一种灵活的方式来创建对象,根据不同的需求,选择适当的工厂模式可以有效地提高代码的可维护性和可扩展性。在实际开发中,我们应该根据具体问题选择合适的设计模式,从而提高软件的质量和开发效率。

7. 学习交流

如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

drawing