设计模式-抽象工厂模式

时间:2022年9月20日

本文档摘抄自:菜鸟教程 ,修改部分

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

一、介绍

意图: 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

主要解决: 主要解决接口选择的问题。

何时使用: 系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

如何解决: 在一个产品族里面,定义多个产品。

关键代码: 在一个工厂里聚合多个同类产品。

应用实例: 工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。

优点: 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点: 产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

注意事项: 产品族难扩展,产品等级易扩展。

二、案例

还是生产机器人,这次机器人的头和身体需要分开创造。所以有两个产品 头和身体,现在有两个工厂,一个创建A1类型的机器人 , 一个创建B1类型的机器人 。

首先定义产品

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

/**
* 机器人的身体
* @author xia17
* @date 2022/9/20
*/public interface RobotBody {

/**
* 获取类型
* @return /
*/ String getType();


}

/**
* 机器人的头
* @author xia17
* @date 2022/9/20
*/public interface RobotHead {

/**
* 获取类型
* @return /
*/ String getType();


}

编写A1、B1型号的头、身体产品

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
/**  
* A1身体
* @author xia17
* @date 2022/9/20
*/public class A1RobotBody implements RobotBody {

@Override
public String getType() {
return "A1";
}

}

/**
* A1头
* @author xia17
* @date 2022/9/20
*/public class A1RobotHead implements RobotHead {
@Override
public String getType() {
return "A1";
}
}

/**
* B1身体
* @author xia17
* @date 2022/9/20
*/public class B1RobotBody implements RobotBody {

@Override
public String getType() {
return "B1";
}

}

/**
* B1头
* @author xia17
* @date 2022/9/20
*/public class B1RobotHead implements RobotHead {
@Override
public String getType() {
return "B1";
}
}

定义抽象工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

/**
* 机器人抽象工厂
* @author xia17
* @date 2022/9/20
*/public interface RobotFactory {

/**
* 生产头
* @return /
*/ RobotHead buildHead();


/**
* 生产身体
* @return /
*/ RobotBody buildBody();




}

实现各A1、B1型号的工厂

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
/**  
* A1型工厂
* @author xia17
* @date 2022/9/20
*/public class A1RobotFactory implements RobotFactory{


@Override
public RobotHead buildHead() {
return new A1RobotHead();
}

@Override
public RobotBody buildBody() {
return new A1RobotBody();
}
}


/**
* B1型工厂
* @author xia17
* @date 2022/9/20
*/public class B1RobotFactory implements RobotFactory{


@Override
public RobotHead buildHead() {
return new B1RobotHead();
}

@Override
public RobotBody buildBody() {
return new B1RobotBody();
}
}

最后编写main方法进行测试

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {  
// A1
RobotFactory robotFactory = new A1RobotFactory();
RobotBody robotBody = robotFactory.buildBody();
RobotHead robotHead = robotFactory.buildHead();

// B1
robotFactory = new B1RobotFactory();
robotBody = robotFactory.buildBody();
robotHead = robotFactory.buildHead();
}

有可能你看到这还是不明所以,我想应该是main方法得测试没有体现抽象工厂得优点,所以我们再试一试。

扩展一个型号容易应该看出来了吧!

我们先定义一个机器人类 Robot

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**  
* 机器人
* @author xia17
* @date 2022/9/20
*/@Getter
public class Robot {


public Robot(RobotHead head, RobotBody body) {
if (!head.getType().equals(body.getType())){
throw new RuntimeException("机器人不合规则");
}
this.head = head;
this.body = body;
}

/** 头 */
private final RobotHead head;

/** 身体 */
private final RobotBody body;

}

这次我们定义一个组装机器人得方法,如果没有使用设计模式那这个方法应该就是Robot得构造方法。采用这样得方法,客户端在使用得时候就必须得注意,不能传不同型号得头和身体。

我们在改进下这个机器人这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**  
* 机器人
* @author xia17
* @date 2022/9/20
*/@Getter
public class Robot {


private Robot(RobotHead head, RobotBody body) {
this.head = head;
this.body = body;
}

public Robot(RobotFactory factory){
this(factory.buildHead(),factory.buildBody());
}

/** 头 */
private final RobotHead head;

/** 身体 */
private final RobotBody body;

}

我们强制创建一个机器人的时候必须传入一个工厂类,这样客户端就无需考虑组装的时候的型号问题。