设计模式之抽象工厂模式

抽象工厂模式

参照blog:工厂方法模式
维基百科:抽象工厂

概念

工厂

在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象。工厂是构造方法的抽象,用来实现不同的分配方案。
工厂对象通常包含一个或多个方法,用来创建这个工厂所能创建的各种类型的对象。这些方法可能接收参数,用来指定对象创建的方式,最后返回创建的对象。
有时,特定类型对象的控制过程比简单地创建一个对象更复杂。在这种情况下,工厂对象就派上用场了。工厂对象可能会动态地创建产品对象的类,
或者从对象池中返回一个对象,或者对所创建的对象进行复杂的配置,或者应用其他的操作。
这些类型的对象很有用。几个不同的设计模式都应用了工厂的概念,并可以使用在很多语言中

抽象工厂模式

抽象工厂模式(英语:Abstract factory pattern)是一种软件开发设计模式。抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。
在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。客户端程序不需要知道(或关心)它从这些内
部的工厂方法中获得对象的具体类型,因为客户端程序仅使用这些对象的通用接口。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。

举个例子来说,比如一个抽象工厂类叫做DocumentCreator(文档创建器),此类提供创建若干种产品的接口,包括createLetter()(创建信件)和createResume()(创建简历)。
其中,createLetter()返回一个Letter(信件),createResume()返回一个Resume(简历)。系统中还有一些DocumentCreator的具体实现类,包括FancyDocumentCreator和ModernDocumentCreator。
这两个类对DocumentCreator的两个方法分别有不同的实现,用来创建不同的“信件”和“简历”(用FancyDocumentCreator的实例可以创建FancyLetter和FancyResume,用ModernDocumentCreator的实例
可以创建ModernLetter和ModernResume)。这些具体的“信件”和“简历”类均继承自抽象类,即Letter和Resume类。客户端需要创建“信件”或“简历”时,先要得到一个合适的DocumentCreator实例,
然后调用它的方法。一个工厂中创建的每个对象都是同一个主题的(“fancy”或者“modern”)。客户端程序只需要知道得到的对象是“信件”或者“简历”,而不需要知道具体的主题,
因此客户端程序从抽象工厂DocumentCreator中得到了Letter或Resume类的引用,而不是具体类的对象引用。

“工厂”是创建产品(对象)的地方,其目的是将产品的创建与产品的使用分离。抽象工厂模式的目的,是将若干抽象产品的接口与不同主题产品的具体实现分离开。
这样就能在增加新的具体工厂的时候,不用修改引用抽象工厂的客户端代码。

使用抽象工厂模式,能够在具体工厂变化的时候,不用修改使用工厂的客户端代码,甚至是在运行时。然而,使用这种模式或者相似的设计模式,
可能给编写代码带来不必要的复杂性和额外的工作。正确使用设计模式能够抵消这样的“额外工作”。

UML类图

抽象工厂模式

应用场景

当一个对象都有相同的约束时,可以使用抽象工厂模式。 
打个比方说,这个工厂的几个产品都需要经过某些共同的步骤和打上相同的商标,这一组产品可以
在一个工厂里面生产,减少很多重复的代码在不同的地方都出现多次。

优缺点

优点

1.针对同一组产品创建新的生产线,只需实现那组产品的抽象工厂接口即可创建新的工厂类。
2.提供一种间接的级别,可以抽象创建相关或依赖的对象的组(family),而不必指定具体的类,
"工厂"对象负责为整个平台系列提供创建服务。客户不直接创建对象,而是通过工厂类来生成这一组对象
3.这种机制使得交换产品系类变得简单,因为工厂类的实例只在程序出现一次初始化,可以通过实
例化不同的具体实例来鼻梁替换整个产品系列

`tips:由于工厂类提供的服务非常普遍,所以通常作为单例实现`

缺点

抽象方法模式的最大缺点就是产品族本身的扩展非常困难。如果在产品族中增加一个新的产品类型,则需要修改多个接口,并影响现已有的工厂类。 

与建造者模式(Builder)的对比

Builder着重于逐步构建复杂的对象。抽象工厂强调一系列产品对象(简单或复杂)。 
Builder作为最后一步返回产品,但就抽象工厂而言,产品会立即返回。

代码实例

这里设计一个工厂生产奔驰汽车和它对应的导航仪两种产品。

(1) 工厂的抽象接口类

1
2
3
4
5
6
7
8
9
10
11
12
public interface BenzFactory {
/**
* 创建奔驰的方法
*/
Benz createCar();

/**
* 创建导航仪的方法
*/
CarNavigator createNavigator();

}

(2) 车辆导航仪的抽象接口类

1
2
3
4
public interface CarNavigator {
void navigatorColor();
void navigatorPrice();
}

(3) 车辆的抽象接口类

1
2
3
4
5
public interface Benz {
void carColor();//设置颜色
void carSpeed();//设置速度
void carPrice();//设置价格
}

(4) 生产C180和其导航仪的工厂类

1
2
3
4
5
6
7
8
9
10
11
public class C180Factory implements BenzFactory {
@Override
public Benz createCar() {
return new BenzC180();
}

@Override
public CarNavigator createNavigator() {
return new C180Navigator();
}
}

(5) 生产B260和其导航仪的工厂类

1
2
3
4
5
6
7
8
9
10
11
public class B260Factory implements BenzFactory {
@Override
public Benz createCar() {
return new BenzB260();
}

@Override
public CarNavigator createNavigator() {
return new B260Navitor();
}
}

(6) 生产C180的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BenzC180 implements Benz {

public BenzC180(){

}
@Override
public void carColor() {
System.out.println("purple");
}

@Override
public void carSpeed() {
System.out.println("101迈");
}

@Override
public void carPrice() {
System.out.println("22块");
}
}

(7) 生产B260的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BenzB260 implements Benz {
public BenzB260(){

}
@Override
public void carColor() {
System.out.println("white");
}

@Override
public void carSpeed() {
System.out.println("102迈");
}

@Override
public void carPrice() {
System.out.println("202块");
}
}

(8) 生产C180导航仪的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class C180Navigator implements CarNavigator {

public C180Navigator(){

}

@Override
public void navigatorColor() {
System.out.println("黑色导航仪");
}

@Override
public void navigatorPrice() {
System.out.println("价格为:66块");
}
}

(9) 生产B260导航仪的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class B260Navitor implements CarNavigator {

public B260Navitor(){

}
@Override
public void navigatorColor() {
System.out.println("绿色的导航仪");
}

@Override
public void navigatorPrice() {
System.out.println("价格为:66元");
}
}

(10) 测试类

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
public class TestDemo {
public static void main(String[] args) {
System.out.println("=====================================\n" +
"生产C180");
//向上转型
BenzFactory c180Factory = new C180Factory();


Benz benz = c180Factory.createCar();
CarNavigator carNavigator = c180Factory.createNavigator();
//因为这几个方法是抽象接口中的方法,所以可以通过Benz,carNavigator调用
benz.carColor();
benz.carPrice();
benz.carSpeed();
carNavigator.navigatorColor();
carNavigator.navigatorPrice();

System.out.println("===========================================");
System.out.println("生产B260");

//向上转型
BenzFactory b260Factory = new B260Factory();


Benz benz2 = b260Factory.createCar();
CarNavigator carNavigator2 = b260Factory.createNavigator();
//因为这几个方法是抽象接口中的方法,所以可以通过Benz,carNavigator调用
benz2.carSpeed();
benz2.carPrice();
benz2.carColor();
carNavigator2.navigatorPrice();
carNavigator2.navigatorColor();
}
}

从以上代码中分析优缺点

优点

生产新的汽车产品和对应的导航仪设备,你只需要创建一个该产品对应的具体工厂和该产品的具体实现类,
变抽象的工厂类和抽象的产品类,具体工厂类和具体产品类需要什么操作已经在抽象接口类定义好了。

缺点

工厂方法的扩展性算是比较好的,而抽象工厂模式的扩展性就没那么好了。 
个工厂需要多创建几个产品对象,那么就需要增加抽象工厂的方法,增加抽象接口的方法后,其他实现了抽象工厂接口方法
都需要改变,才能正常编译。

代码示例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
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
// class CPU
abstract class CPU {}

// class EmberCPU
class EmberCPU extends CPU {}

// class EnginolaCPU
class EnginolaCPU extends CPU {}

// class MMU
abstract class MMU {}

// class EmberMMU
class EmberMMU extends MMU {}

// class EnginolaMMU
class EnginolaMMU extends MMU {}

// class EmberFactory
class EmberToolkit extends AbstractFactory {
@Override
public CPU createCPU() {
return new EmberCPU();
}

@Override
public MMU createMMU() {
return new EmberMMU();
}
}

// class EnginolaFactory
class EnginolaToolkit extends AbstractFactory {
@Override
public CPU createCPU() {
return new EnginolaCPU();
}

@Override
public MMU createMMU() {
return new EnginolaMMU();
}
}

enum Architecture {
ENGINOLA, EMBER
}

abstract class AbstractFactory {
private static final EmberToolkit EMBER_TOOLKIT = new EmberToolkit();
private static final EnginolaToolkit ENGINOLA_TOOLKIT = new EnginolaToolkit();

// Returns a concrete factory object that is an instance of the
// concrete factory class appropriate for the given architecture.
static AbstractFactory getFactory(Architecture architecture) {
AbstractFactory factory = null;
switch (architecture) {
case ENGINOLA:
factory = ENGINOLA_TOOLKIT;
break;
case EMBER:
factory = EMBER_TOOLKIT;
break;
}
return factory;
}

public abstract CPU createCPU();

public abstract MMU createMMU();
}

public class Client {
public static void main(String[] args) {
AbstractFactory factory = AbstractFactory.getFactory(Architecture.EMBER);
CPU cpu = factory.createCPU();
}
}

这段代码更加清晰

实例分析

抽象工厂的目的是提供一个用于创建相关对象族的界面,而不需要指定具体的类。  
这种模式可以在日本汽车制造中使用的钣金冲压设备中找到。 
冲压设备是一个创建汽车车身零件的抽象工厂。 
同样的机器被用来为不同车型的右手门,左手门,右前挡泥板,左前挡泥板,引擎盖等。 
通过使用滚轮来更换冲压模具,可以在三分钟内改变机器生产的产品类别。

![示例图片](/images/java/sample_abstract_factory.jpg)