设计模式--抽象工厂模式

上一篇介绍的工厂方法模式中虽然解决了bmw汽车生产问题,并且也在本地初步实现了设计-生产-测试-销售一体化的目标,并且在当地的业绩还不错;当时现在高层领导看到了中国是一个很大的市场,所以计划在中国制造BMW汽车,经过开会商议,形成了以下会议纪要:

  1. 不能把目前在慕尼黑工厂生产的汽车直接运到中国去卖,这样太麻烦,代价太大;与其这样还不如把所有配件运输到中国,然后组装呢,反正你都需要把配件运输过去,不然客户购买汽车后发生故障了要修理,更换配件怎么办;但是把所有配件运输到中国也不太可行,因为这样代价也太大,成本太高了,我们需要确保有足够的价格优势,这样客户才能买账啊。
  2. 需要把汽车进行拆分成不同的零部件,然后针对零部件在中国建立工厂,然后进行组装销售

一、汽车零部件拆解

一辆汽车都是由发动机、方向盘、车架、车轮组成;也就是说一辆车通过车架整合之后,我启动发动机,转动方向盘,随着车轮的转动,这辆汽车就开走了;所以汽车部件如下:

  • 发动机
  • 方向盘
  • 车架
  • 车轮

二、抽象配件

我们明白了具体的汽车的部件构造之后,就开始在中国建造第一个BMW工厂了,建造工厂之前,我们先需要先把各个部件抽象成不同的产品出来(除了发动机)。

1、发动机

每一辆汽车的心脏就是这个发动机,所以我们把发动机这个配件抽象成一个独立的子产品意义上的配件,不过发动机也分好多种,最突出的一点就是动力,比如300马力、500马力的动力等等。

  • 发动机接口-Engine
1
2
3
4
5
6
7
8
9
/**
* 通用发动机产品接口
* @author mapingsheng
*
*/
public interface Engine {
Engine engineInfo();
}
  • 300马力发动机具体类-Engine300
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 300马力的发动机
* @author mapingsheng
*
*/
public class Engine300 implements Engine {
Engine300(){
System.out.println("300马力的发动机");
}
@Override
public Engine engineInfo() {
return new Engine300();
}
}
  • 500马力发动机具体类-Engine500
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 500马力的发动机
* @author mapingsheng
*
*/
public class Engine500 implements Engine {
Engine500(){
System.out.println("500马力的发动机");
}
@Override
public Engine engineInfo() {
return new Engine500();
}
}

2、方向盘

因为方向盘配件有很多种,比如多功能方向盘(上面各种控制按钮)、普通方向盘(就一个喇叭),所以我们把方向盘抽象成一个通用的接口

  • 方向盘接口-Steering
1
2
3
4
5
6
7
8
9
/**
* 方向盘产品通用接口
* @author mapingsheng
*
*/
public interface Steering {
public Steering steeringInfo();
}
  • 多功能版方向盘具体类-SteeringMulti
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 多功能版方向盘
* @author mapingsheng
*
*/
public class SteeringMulti implements Steering {
SteeringMulti(){
System.out.println("多功能方向盘");
}
@Override
public Steering steeringInfo() {
return new SteeringMulti();
}
}
  • 简单版方向盘具体类-SteeringSimple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 简单款方向盘
* @author mapingsheng
*
*/
public class SteeringSimple implements Steering {
SteeringSimple(){
System.out.println("简单方向盘");
}
@Override
public Steering steeringInfo() {
return new SteeringSimple();
}
}

3、车轮

车轮是汽车可以平稳行驶在路上的前提,当然车轮也分为很多种,比如规格、材质等等,我们就简单的区分大号、中号的车轮。

  • 车轮接口-Wheel
1
2
3
4
5
6
7
8
9
/**
* 通用车轮产品接口
* @author mapingsheng
*
*/
public interface Wheel {
Wheel wheelInfo();
}
  • 大号车轮具体类-WheelL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 大号铝合金车轮
* @author mapingsheng
*
*/
public class WheelL implements Wheel {
WheelL(){
System.out.println(" 大号(L)车轮-铝合金");
}
@Override
public Wheel wheelInfo() {
return new WheelL();
}
}
  • 中号车轮具体类-WheelM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 普通号车轮-不锈钢
* @author mapingsheng
*
*/
public class WheelM implements Wheel {
WheelM(){
System.out.println(" 中号(M)车轮-不锈钢");
}
@Override
public Wheel wheelInfo() {
return new WheelM();
}
}

4、车架

车架就好比一个电脑的主板,把各个配件固定在架子上面,然后用发动机驱动车轮行驶,用方向盘控制行驶的方向(废话)。当然不同的车车架也有很多规格型号,比如微型车、中型车、加长版等等,我们这里暂且分为加长版和普通版吧。

  • 车架接口-Carframe
1
2
3
4
5
6
7
8
9
/**
* 通用车架产品接口
* @author mapingsheng
*
*/
public interface Carframe {
Carframe carframeInfo();
}
  • 普通款车架具体类-CarframeM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 普通版型车架
* @author mapingsheng
*/
public class CarframeM implements Carframe {
CarframeM(){
System.out.println(" 普通版(M)车架");
}
@Override
public Carframe carframeInfo() {
return new CarframeM();
}
}
  • 加长版车架具体类-CarframeL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 加长型车架
* @author mapingsheng
*/
public class CarframeL implements Carframe {
CarframeL(){
System.out.println(" 加长版(L)车架");
}
@Override
public Carframe carframeInfo() {
return new CarframeL();
}
}

三、配件工厂

我们在上一步中抽象了具体的配件接口和具体的配件产品类,那么下一步我们需要抽象一个配件生产工厂,由于需要在中国建立工厂,那么我们就建立一个德国配件工厂、中国配件工厂;每一个配件工厂都生产发动机、方向盘、车轮、车架四大配件,只是每一个配件长生产的配件的型号规格不同。

1、通用配件工厂接口-PartsFactory

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 通用配件工厂接口
* @author mapingsheng
*
*/
public interface PartsFactory {
Carframe createCarframe();
Engine createEngine();
Steering createSteering();
Wheel createWheel();
}

2、德国配件工厂-PartsFactoryDE

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
/**
* 德国的配件工厂
* @author mapingsheng
*/
public class PartsFactoryDE implements PartsFactory {
PartsFactoryDE(){
System.out.println("德国汽车配件厂");
}
@Override
public Carframe createCarframe() {
return new CarframeL();
}
@Override
public Engine createEngine() {
return new Engine500();
}
@Override
public Steering createSteering() {
return new SteeringMulti();
}
@Override
public Wheel createWheel() {
return new WheelL();
}
}

2、中国配件工厂-PartsFactoryCN

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
/**
* 中国的配件工厂
* @author mapingsheng
*/
public class PartsFactoryCN implements PartsFactory {
PartsFactoryCN(){
System.out.println("中国汽车配件厂");
}
@Override
public Carframe createCarframe() {
return new CarframeM();
}
@Override
public Engine createEngine() {
return new Engine300();
}
@Override
public Steering createSteering() {
return new SteeringSimple();
}
@Override
public Wheel createWheel() {
return new WheelM();
}
}

三、产品组装工厂

我们把配件工厂组建完成后,就可以生产配件了,但是我们需要把各个不同的配件组装在一起成为不同的具体产品(BMWx1、BMWx6)等等,我们暂且指定义两个型号的产品-BMWx1、BMWx6

1、通用BMW系列产品接口

1
2
3
4
5
6
7
8
/**
* BMW汽车产品接口
* @author mapingsheng
*/
public interface BMW {
void getBMWinfo();
}

2、具体型号的BMW产品类-BMWx1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 具体型号的BMW产品类-BMWx1
* @author mapingsheng
*/
public class BMWx1 implements BMW {
PartsFactory partsFactory;
BMWx1(PartsFactory partsFactory){
this.partsFactory = partsFactory;
}
@Override
public void getBMWinfo() {
System.out.println("BMWx1系轿车");
Engine engine = partsFactory.createEngine();
Carframe carframe = partsFactory.createCarframe();
Steering steering = partsFactory.createSteering();
Wheel wheel = partsFactory.createWheel();
}
}

3、具体型号的BMW产品类-BMWx6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 具体型号的BMW产品类-BMWx6
* @author mapingsheng
*/
public class BMWx6 implements BMW {
PartsFactory partsFactory;
BMWx6(PartsFactory partsFactory){
this.partsFactory = partsFactory;
}
@Override
public void getBMWinfo() {
System.out.println("BMWx6系轿车");
Engine engine = partsFactory.createEngine();
Carframe carframe = partsFactory.createCarframe();
Steering steering = partsFactory.createSteering();
Wheel wheel = partsFactory.createWheel();
}
}

4、建立客户工厂-CustomerFactory

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
/**
* 建立客户工厂,对外提供预定方法
* @author mapingsheng
*/
public class CustomerFactory {
/**
* 预定汽车服务
* @param type
* @return
*/
public BMW orderBMW(String type){
return createBMW(type);
}
/**
* 汽车生产服务,根据不同型号生产不同类型的汽车
* @param type
* @return
*/
public BMW createBMW(String type){
BMW bmw = null;
if("X1".equals(type)){
bmw = new BMWx1(new PartsFactoryCN());
}else if("X6".equals(type)){
bmw = new BMWx6(new PartsFactoryDE());
}
return bmw;
}
}

四、建立测试类-Test

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
//实例化一个客户工厂,可以想象成一个网站,通过网站预定汽车
CustomerFactory produce = new CustomerFactory();
BMW bmwX1 = produce.orderBMW("X1");//预定一个BMWx1系列的轿车,其实x1使用的是中国配件工厂生产的配件
bmwX1.getBMWinfo();
BMW bmwX6 = produce.orderBMW("X6");//预定一个BMWx6系列的轿车,其实x6使用的是德国配件工厂生产的配件
bmwX6.getBMWinfo();
}
}

以上代码运行结果:

中国汽车配件厂

BMWx1系轿车

300马力的发动机

普通版(M)车架

简单方向盘

中号(M)车轮-不锈钢


德国汽车配件厂

BMWx6系轿车

500马力的发动机

加长版(L)车架

多功能方向盘

大号(L)车轮-铝合金

通过上面的示例演示了抽象工程的使用方式,其实说白了,就是把后期会变化的模块都进行抽象独立出来,以方便后期扩展;所以个人觉得这个抽象粒度是不固定的,根据你的业务场景来决定的,比如上面的仅仅抽象了4大类配件来组装一辆汽车,其实抽象的粒度应该更细,比如发动机、车架、车轮等等都可以再细化的抽象。

在上面的抽象过程中其实是分成两个维度(同一层级、不同层级)去抽象的,比如四大配件产品的抽象,是属于统一层级结构的抽象,也称为平行的类层级;再往上一级还有配件生产工厂的抽象,配件工厂再往上一级还有具体型号的产品;所以说我们在运用抽象工厂时应该遵循下层(具体型号的配件)不能依赖上层(具体型号的BMW汽车)。

在设计抽象工厂应用时还应该遵循:

变量不可以持有具体类的引用–如果使用new,就会持有具体类的引用,你可以改用工厂来避开这样的做法,实质上就是把多个new集中在了一个工厂方法中

不要让类派生自具体类–如果派生自具体类,你就会依赖具体类。请派生自一个抽象(接口或者抽象类)

其实上面的代码中我们可以发现抽象工厂的方法中实则是工厂方法的一种升级包装。

五、小结

所有工厂都是用来封装对象的创建;

工厂方法使用继承或实现把对象的创建委托给子类或实现类

抽象工厂使用使用对象组合,对象的创建被实现在工厂接口所暴露出来的的方法中

所有工厂模式都是通过减少应用程序和具体类之间的依赖促进松耦合。

抽象工厂模式-提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类(比如上面的例子中我们从底层向高层进行抽象了配件接口->产品接口,配件工厂的组装仅仅依赖抽象的配件接口,而不是依赖具体的配件类)

工厂方法模式-定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,把类的实例化推迟到子类中