设计模式(3)——抽象工厂模式

本文介绍抽象工厂模式的概念和应用。

基本思想和原则

为创建一组相关或相互依赖的对象提供一组接口,而且无须指定它们的具体类。

高层模块不应直接依赖低层模块,应该依赖其抽象,工厂就是这个抽象。

动机

让我们考虑产品族和产品类型这两个概念。举个产品族的例子,苹果和戴尔两家公司都生产计算机,计算机是一种统称,是一个抽象概念,这是产品族。其中计算机又可以分为服务器、台式机、笔记本等,这是计算机的类型,一个具体概念,这是产品类型。因此产品族和产品类型的对比如下表:

名称 抽象/具体 概念方向
产品族 抽象 横向
产品类型 具体 纵向

实现

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
public abstract class AppleComputer {
public abstract void run();
}

public class AppleDesktop extends AppleComputer {
@Override
public void run() {
System.out.println("AppleDesktop run!");
}
}

public class AppleNotebook extends AppleComputer {
@Override
public void run() {
System.out.println("AppleNotebook run!");
}
}

public abstract class DellComputer {
public abstract void run();
}

public class DellDesktop extends DellComputer {
@Override
public void run() {
System.out.println("DellDeskComputer run!");
}
}

public class DellNotebook extends DellComputer {
@Override
public void run() {
System.out.println("DellNotebook run!");
}
}

public abstract class ComputerFactory {
public abstract AppleComputer createAppleComputer();
public abstract DellComputer createDellComputer();
}

public class DesktopFactory extends ComputerFactory {
@Override
public AppleComputer createAppleComputer() {
return new AppleDesktop();
}

@Override
public DellComputer createDellComputer() {
return new DellDesktop();
}
}

public class NotebookFactory extends ComputerFactory {
@Override
public AppleComputer createAppleComputer() {
return new AppleNotebook();
}

@Override
public DellComputer createDellComputer() {
return new DellNotebook();
}
}

public class Test {
public static void main(String[] args) {
ComputerFactory desktopFactory = new DesktopFactory();
ComputerFactory notebookFactory = new NotebookFactory();

AppleComputer appleDesktop = desktopFactory.createAppleComputer();
AppleComputer appleNotebook = notebookFactory.createAppleComputer();

DellComputer dellDesktop = desktopFactory.createDellComputer();
DellComputer dellNotebook = notebookFactory.createDellComputer();

appleDesktop.run();
appleNotebook.run();
dellDesktop.run();
dellNotebook.run();
}
}

运行后输出:

1
2
3
4
AppleDesktop run!
AppleNotebook run!
DellDeskComputer run!
DellNotebook run!

优点

封装性好,将高层模块和具体实现类解耦,高层模块不需要关心具体实现类的细节,只需要和工厂打交道,由工厂去创建对象。

缺点

抽象工厂模式很难对产品族进行扩展,来看看上面的例子如果要增加一个惠普品牌的计算机需要做哪些新增或修改:

  1. 新增HPDesktop类。
  2. 新增HPNotebook类。
  3. 修改ComputerFactory,在其中添加createHPComputer这个方法。
  4. 修改DesktopFactory,实现createHPComputer这个方法。
  5. 修改NotebookFactory,实现createHPComputer这个方法。

可以看到,2个新增,3个修改,工作量不仅很大,而且还要改动原来已经可以工作的类。也就是说,想要扩展产品族,就需要对原来的契约双方都做修改,很明显违反了开闭原则。

相反,使用抽象工厂模式对产品类型进行扩展要容易得多,上面的例子如果要增加一个服务器类型的计算机需要的新增或修改:

  1. 新增AppleServer类。
  2. 新增DellServer类。
  3. 新增ServerFactory类。

可以发现使用抽象工厂模式扩展产品类型都是新增,不需要修改原来的代码,符合开闭原则。