设计模式——工厂模式

工厂模式概念

实例化对象,用工厂方法来代替new操作

工厂模式包括简单工厂模式、工厂方法模式、抽象工厂模式

抽象工厂模式是工厂方法模式的拓展

工厂模式的意图

实现了创建者和调用者的分离

定义一个接口来创建对象,让子类决定哪些对象需要被实例化

工厂方法模式把实例化的工作推迟到子类去实现

什么情况需要工厂模式

  • 有一组类似的对象需要被创建
  • 在编码时不能预见需要创建哪种类的实例
  • 系统需要考虑拓展性

工厂模式详细分类

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

遵循的面向对象设计原则(了解)

  1. 开闭原则:对拓展开发,对修改关闭。在理想的状态下,当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类就可以,不需要修改原来的任何一行代码
  2. 依赖倒转原则:针对接口而不是针对实现编程。该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代
  3. 迪米特法则:米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。只与朋友通信,避免和陌生人通信

没有工厂的情况

接口:车

1
2
3
public interface Car {
void run();
}

实现类:宝马

1
2
3
4
5
6
public class BmwCar implements Car {
@Override
public void run() {
System.out.println("宝马发动了");
}
}

实现类:比亚迪

1
2
3
4
5
6
public class BydCar implements Car {
@Override
public void run() {
System.out.println("比亚迪发动了");
}
}

测试类:

1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
Car car1 = new BmwCar();
Car car2 = new BydCar();
}
}

分析:

在没有工厂模式的情况下,调用者需要知道实现的接口,还需要知道其每一个实现类的实现方式

若上文演示的实现类都需要设置复杂的参数,那么对于调用者来说是违反迪米特法则

(一)简单工厂模式

我们在其他类都保持原样的情况下,添加这样一个工厂如下:

简单工厂:

1
2
3
4
5
6
7
8
9
10
public class SimpleFactory {
public static Car getCar(String carName){
if ("比亚迪".equals(carName)){
return new BydCar();
}else if ("宝马".equals(carName)){
return new BmwCar();
}
return null;
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
Car car1 = SimpleFactory.getCar("比亚迪");
Car car2 = SimpleFactory.getCar("宝马");

if (car1 != null && car2 != null){
car1.run();
car2.run();
}
}
}

分析:

在简单工厂方法当中,我们调用者已经不需要知道实现类被创造的细节,但是依然存在一个问题:

简单工厂是依赖ifelse判断来决定创造哪一个实现类的对象的,那么如果我们需要增加新的实现类、删除原有实现类,都要频繁修改工厂类

这违反了上述的开闭原则,不过在业务不需要频繁变更的项目当中,已经可以使用了

(二)工厂方法模式

  • 为了避免简单工厂模式不遵守开闭原则的缺点
  • 工厂方法模式和简单工厂模式的不同点在于:简单工厂模式往往只有一个工厂类,而工厂方法模式有一组实现了相同接口的工厂类

在不改变没有使用工厂模式情况下的代码的前提下,我们创建这样的工厂方法:

首先,创建一个工厂的接口,用于规范一系列工厂的行为

工厂的接口:

1
2
3
public interface Factory {
Car getCar();
}

其次,我们创建各个种类对象的分工厂,这些分工厂统一实现工厂接口

分工厂:

1
2
3
4
5
6
public class FactoryBmw implements Factory {
@Override
public Car getCar() {
return new BmwCar();
}
}
1
2
3
4
5
6
public class FactoryByd implements Factory {
@Override
public Car getCar() {
return new BydCar();
}
}

这些分工厂一样可以隐藏实现这些对象的细节,遵循迪米特法则

测试类:

1
2
3
4
5
6
7
8
9
10
11
public class Test {
public static void main(String[] args) {
Car car1 = new FactoryBmw().getCar();
Car car2 = new FactoryByd().getCar();

if (car1 != null && car2 != null){
car1.run();
car2.run();
}
}
}

分析:

使用工厂方法模式,如果需要添加新的实现类,只需要创建新的分工厂实现工厂接口即可;如果需要删除实现类,只需要删除对应的分工厂即可

这样的设计完美实现了开闭原则的要求

(三)抽象工厂模式

  • 用于生产不同产品族的全部产品(对于增加单个产品无能为力,支持增加一个产品族)
  • 抽象工厂模式和工厂方法模式处理的是不同的场景。在存在多个业务分类时,可以使用此模式创造一整个族的产品

由于抽象工厂模式的复杂性,我们这里使用全新的案例分析:

产品族:

汽车产品族1:低端轮胎、低端座椅、低端发动机

汽车产品族2:高端轮胎、高端座椅、高端发动机

抽象工厂模式就是用于直接生成一个产品族内全部对象的工厂模式

发动机模块:

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
/**
* 发动机接口
*/
public interface Engine() {
void run();
void start();
}

/**
* 豪华发动机
*/
class LuxuryEngine() implements Engine {
@Override
public void run() {
System.out.println("极速快");
}

@Override
public void start() {
System.out.println("提速快");
}
}

/**
* 低端发动机
*/
class LowEngine() implements Engine {
@Override
public void run() {
System.out.println("极速慢");
}

@Override
public void start() {
System.out.println("提速慢");
}
}

座椅模块:

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
/**
* 座椅接口
*/
public interface Seat() {
void massage();
}

/**
* 豪华座椅
*/
class LuxurySeat() implements Seat {
@Override
public void massage() {
System.out.println("按摩舒服");
}
}

/**
* 低端座椅
*/
class LowSeat() implements Seat {
@Override
public void massage() {
System.out.println("按摩不舒服");
}
}

轮胎模块:

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
/**
* 轮胎接口
*/
public interface Tyre() {
void revolve();
}

/**
* 豪华轮胎
*/
class LuxuryTyre() implements Tyre {
@Override
public void revolve() {
System.out.println("旋转磨损低");
}
}

/**
* 低端轮胎
*/
class LowTyre() implements Tyre {
@Override
public void revolve() {
System.out.println("旋转磨损高");
}
}

下面就要创建整合我们的产品族中每一个模块的整合对象:Car了,汽车工厂接口负责定义每一个汽车分工厂的规范

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
/**
* 汽车工厂接口
*/
public interface CarFactory {
void createEngine();
void createSeat();
void createTyre();
}

/**
* 高端汽车工厂
*/
class LuxuryCarFactory implements CarFactory {
@Override
public void createEngine() {
return new LuxuryEngine();
}
@Override
public void createSeat() {
return new LuxurySeat();
}
@Override
public void createTyre() {
return new LuxuryTyre();
}
}

/**
* 低端汽车工厂
*/
class LowCarFactory implements CarFactory {
@Override
public void createEngine() {
return new LowEngine();
}
@Override
public void createSeat() {
return new LowSeat();
}
@Override
public void createTyre() {
return new LowTyre();
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
// 我们需要一辆高档车
CarFactory factory = new LuxuryCarFactory();
Engine engine = factory.createEngine();
Seat seat = factory.createSeat();
Tyre tyre = factory.createTyre();
engine.run(); // 极速快
seat.massage(); // 按摩舒服
tyre.revolve(); // 旋转磨损低
}
}

分析:

按照上文演示的测试类,即可创造出一个高端汽车产品族所需的所有模块。如果我们需要一辆中档的汽车(混合高端引擎和低端座椅),那么我们仅需要创建一个中档汽车工厂,并实现汽车工厂接口,在其对应方法中创造出对应产品,即可创造一条新的产品族

在实际应用当中,如果我们的业务不需要创建一个庞大的产品族,抽象工厂模式很少用到

总结

工厂模式的应用场景:

  1. JDK中Calendar的getInstance()方法
  2. JDBC中Connection对象的获取
  3. Hibernate当中,使用SQLSessionFactory创建SQLSession
  4. Spring中,IOC容器创建管理Bean对象
  5. XML文件解析时,DocumentBuilderFactory创建解析器对象
  6. 反射中Class对象的newInstance()方法