GoF设计模式(四):Abstract Factory Pattern 抽象工厂模式

之前我们介绍了Factory Pattern工厂模式,其较适合于只有一种类型产品的场景。但有些时候一家工厂可能会生产多种类型的产品。比如家电厂无论海尔还是美的,都会生产冰箱、洗衣机、空调等多种不同类型的产品。这个时候,就可以应用我们这里所说的Abstract Factory Pattern抽象工厂模式

abstract.jpeg

简介

这里我们以PC厂商为例来进入引入介绍。对于Dell、HP这些PC厂商而言,其会去生产销售显示器、鼠标、键盘等这些外设。换句话说,在这里,一家工厂将不再是只提供一种产品了,而是会去提供多种产品。所以对于Abstract Factory Pattern抽象工厂模式而言,其和Factory Pattern工厂模式相比,最大的不同就在于抽象工厂角色具体工厂角色部分。这里我们先来简单介绍下Abstract Factory Pattern抽象工厂模式下的角色

  1. 抽象产品角色:其定义了具体产品的实现类所具有的共有方法,一般通过接口实现。本文中即为Monitor、Keyboard、Mouse接口类。它们分别定义了各自产品的公共特性
  2. 具体产品角色:其是抽象产品角色的具体实现类,即是客户真正需要的产品。这里即为不同PC厂家所提供的外设产品
  3. 抽象工厂角色:其定义了不同PC厂商的工厂的通用接口,用以提供该PC厂商所生产的各种不同种类的产品。故这也是其于Factory Pattern工厂模式最大的区别之处,后者(工厂模式)只提供一种类型的产品
  4. 具体工厂角色:其是抽象工厂的具体实现类,一个具体的工厂类负责提供其生产多种类型的产品

实践

现在让我们利用上文所提到的不同PC厂商(Dell、HP)的多种外设产品(键盘、显示器、鼠标)为例,通过代码进行地具体演示。相信大家对于抽象产品角色、具体产品角色的设计应该是比较熟悉的了。这里我们先给出显示器的接口及具体实现类

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 Monitor {
void display();
}
...
/**
* 具体产品角色: HP 显示器
*/
public class HpMonitor implements Monitor {
@Override
public void display() {
System.out.println("HP 显示器正在显示");
}
}
...
/**
* 具体产品角色: DELL 显示器
*/
public class DellMonitor implements Monitor {
@Override
public void display() {
System.out.println("DELL 显示器正在显示");
}
}

对于键盘、鼠标产品而言,同理如下

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
/**
* 抽象产品角色: 键盘
*/
public interface Keyboard {
void use();
}
...
/**
* 具体产品角色: HP 键盘
*/
public class HpKeyboard implements Keyboard {
@Override
public void use() {
System.out.println("HP 键盘正在使用");
}
}
...
/**
* 具体产品角色: DELL 键盘
*/
public class DellKeyboard implements Keyboard {
@Override
public void use() {
System.out.println("DELL 键盘正在使用");
}
}


/**
* 抽象产品角色: 鼠标
*/
public interface Mouse {
void move();
}
...
/**
* 具体产品角色: HP 鼠标
*/
public class HpMouse implements Mouse {
@Override
public void move() {
System.out.println("HP 鼠标正在移动");
}
}
...
/**
* 具体产品角色: DELL 鼠标
*/
public class DellMouse implements Mouse {
@Override
public void move() {
System.out.println("DELL 鼠标正在移动");
}
}

现在我们来定义一个抽象工厂角色——即PC厂商的接口类PCFactory,其可以提供各种不同类型产品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 抽象工厂角色:PC工厂
*/
public interface PCFactory {
/**
* 提供显示器产品
* @return
*/
Monitor getMonitor();

/**
* 提供鼠标产品
* @return
*/
Mouse getMouse();

/**
* 提供键盘产品
* @return
*/
Keyboard getKeyboard();
}

有了抽象工厂角色,那么具体的工厂角色就简单了。本文就是Dell、HP的工厂类DellFactory、HpFactory,Client通过各自PC厂商的工厂类就可以获取相应的产品了

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
/**
* 具体工厂角色:Dell工厂
*/
public class DellFactory implements PCFactory{
@Override
public Monitor getMonitor() {
return new DellMonitor();
}

@Override
public Mouse getMouse() {
return new DellMouse();
}

@Override
public Keyboard getKeyboard() {
return new DellKeyboard();
}
}
...
/**
* 具体工厂角色:HP工厂
*/
public class HpFactory implements PCFactory{
@Override
public Monitor getMonitor() {
return new HpMonitor();
}

@Override
public Mouse getMouse() {
return new HpMouse();
}

@Override
public Keyboard getKeyboard() {
return new HpKeyboard();
}
}

至此我们的抽象工厂模式就已经实现完毕了,那么现在就通过一个测试用例来看看是否可以正常工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
System.out.println("--------- Test 1 ---------");
PCFactory hpFactory = new HpFactory();
Monitor monitor = hpFactory.getMonitor();
Mouse mouse = hpFactory.getMouse();
Keyboard keyboard = hpFactory.getKeyboard();
monitor.display();
mouse.move();
keyboard.use();

System.out.println("--------- Test 2 ---------");
PCFactory dellFactory = new DellFactory();
monitor = dellFactory.getMonitor();
mouse = dellFactory.getMouse();
keyboard = dellFactory.getKeyboard();
monitor.display();
mouse.move();
keyboard.use();
}
}

测试结果如下符合我们的预期

figure 1.png

开闭原则的倾斜性

开闭原则是指对扩展开放、对修改封闭,通过扩展的方式来实现功能的增强。而在Abstract Factory Pattern抽象工厂模式下,如果我们期望在本文例子中再增加一个PC厂商Apple,那么就只需要添加苹果的工厂类及相关产品类即可,即此种功能拓展是符合开闭原则的;但如果是期望增加一种新的外设产品,比如光驱,那么我们不仅需要添加相关的产品类,更重要的是我们还需要在现有的工厂类(包括抽象工厂角色和具体工厂角色)中添加getCdDriver()方法,而这种功能拓展显然就违背了开闭原则。综上所述,在Abstract Factory Pattern抽象工厂模式中开闭原则是有倾斜性的

参考文献

  1. Head First 设计模式 弗里曼著
0%