浅谈工厂模式

简介

工厂模式解决的是频繁的修改某一些 new 操作,隐藏真实的创建过程,方便以后更加快速的新增和扩展,简单来说就是维护一类关系。

简单工厂:

把对象的创建放到一个Util中,通过不同的入参来创建不同的类。这也是日常编码中经常用到的,不过缺点就是每次新增一个类的时候,都需要修改if/else判断,有点繁琐。

工厂方法:

将含有相同属性的类抽象成一个工厂,这一个工厂只负责自己的对象的创建,对修改关闭,对扩展开放。

抽象工厂:

对类的操作进一步的划分,将某种类的操作再次进行抽象化。

代码

假设现在要开发一个餐厅点餐系统,首先肯定是有顾客以及食物。为了以后的扩展性,在这里通过三种工厂方法来实现不同的需求

简单工厂

现在有两个类,User 和 Food,此时有一个需求是人需要吃食物,可能你毫不犹豫的写下了如下代码:

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 Potato {
private int temperaturel;

private String name;

public Potato(String name) {
this.name = name;
}

public void cook(){
System.out.println("加热中");
this.temperaturel = 100;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

public class User {
public static void main(String[] args) {
new User().dinner();
}

private void dinner(){
Potato potato = new Potato("土豆");
potato.cook();
System.out.println("吃晚餐" + potato.getName());
}
}

后来有一天需求变了,现在需要再加一个食物以供客户选择,后来你想了下,决定写一个 Util 类。如下:

1
2
3
4
5
6
7
8
9
public static Object getFood(String name){
if(name != null){
if("Potato".equals(name)){
return new Potato("土豆");
} else if("Tomato".equals(name)){
return new Tomato("西红柿");
}
}
}

这段代码明显的有一个问题就是,返回的 Object 在另一个地方进行强制转换的时候,由于不知道返回的是 Potato 还是 Tomato,所以很容易抛出转换异常,所以此时,再继续优化这段代码,由于土豆和西红柿都是食物的一种,于是可以直接抽象一个接口出来。这样土豆和马铃薯都实现这个接口。如下:

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 Food {

void cook();

String getName();
}
public class Potato implements Food{
private int temperaturel;

private String name;

public Potato(String name) {
this.name = name;
}

@Override
public void cook(){
System.out.println("加热中");
this.temperaturel = 100;
}

@Override
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
public class Tomato implements Food{
private int temperaturel;

private String name;

public Tomato(String name) {
this.name = name;
}

@Override
public void cook(){
System.out.println("加热中");
this.temperaturel = 100;
}

@Override
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

这个时候呢,修改 Util 类,让它返回 Food 这个抽象,然后通过实例化子类,来实现模版的调用,于是继续修改 Util 类。如下:

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
public class FoodUtil {
public static Food getFood(String name){
if(name != null){
if("Potato".equals(name)){
return new Potato("土豆");
} else if("Tomato".equals(name)){
return new Tomato("西红柿");
}
}
return null;
}
}

public class User {
public static void main(String[] args) {
new User().dinner("Potato");
new User().dinner("Tomato");
}

private void dinner(String name){
Food food = FoodUtil.getFood(name);
food.cook();
System.out.println("吃晚餐" + food.getName());
}
}

但是随着食物种类的不断增加,每一次新增一种食物,你都要去修改getFood这个方法,每次修改完毕之后,你还必须对其进行回归测试,万一改错了,或者equals方法调用者的key重复了,那就会影响到系统的运行了。而且这也不符合设计模式里面的开闭原则,对扩展开放,对修改关闭。所以此时继续对这个 Util 进行修改。

1
2
3
4
5
6
7
public static Food getFood(Class<? extends Food> clazz) throws IllegalAccessException, InstantiationException {
if(clazz != null){
// 在这里方便演示是设计模式,所以在Tomato 和 Potato加入了无参的构造器
return clazz.newInstance();
}
return null;
}

这就是一个简单工厂模式,简单工厂模式严格意义上来讲并不属于设计模式其中之一,但是这也是我们在开发过程中经常使用的一种方式,用于避免一些无意义的 new 操作。
接下来就来讲下工厂方法模式。

工厂方法模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//抽象的一个工厂
public interface FoodFactory {
Food createFood();
}
// 西红柿工厂
public class PotatoFactory implements FoodFactory {
@Override
public Food createFood() {
return new Potato();
}
}
// 土豆工厂
public class TomatoFactory implements FoodFactory {
@Override
public Food createFood() {
System.out.println("清洗干净");
System.out.println("切好");
return new Tomato();
}
}

这个时候,假设我们要使用的话,则直接通过 new 一个子类即可,如下代码:

1
2
3
4
5
6
7
8
public class User {
public static void main(String[] args) {
FoodFactory potatoFactory = new PotatoFactory();
FoodFactory tomatoFactory = new TomatoFactory();
tomatoFactory.createFood().cook();
potatoFactory.createFood().cook();
}
}

这样做的好处是,对扩展开放,当需要添加其他食物的时候,直接新建一个Factory类然后实现FoodFactory接口即可,这就是工厂模式的好处。

抽象工厂模式

抽象工厂模式是对工厂的更进一步抽象,例如 土豆 和 西红柿是一组产品,因为它们都同属于食物。但是现在有两个地方都可以生产 土豆 和 西红柿,A地的是金色的,B地生产的是银色的,于是此时 A 和 B 就产生了一个产品等级。但是A 和 B都是具有一组通用的特性,即使可以生产不同的颜色 土豆 和 西红柿。

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
public interface PlantFactory {
Tomato plantTomato();

Potato plantPotato();
}

public class AFactory implements PlantFactory{
@Override
public Tomato plantTomato() {
System.out.println("plantTomato By A");
return new Tomato();
}

@Override
public Potato plantPotato() {
System.out.println("plantPotato By A");
return new Potato();
}
}

public class BFactory implements PlantFactory {
@Override
public Tomato plantTomato() {
System.out.println("plantTomato By B");
return new Tomato();
}

@Override
public Potato plantPotato() {
System.out.println("plantPotato By B");
return new Potato();
}
}

public class User {
public static void main(String[] args) {
PlantFactory plantFactory = new AFactory();
plantFactory.plantPotato().cook();
}
}

总的来说,这三种设计模式中,除了抽象工厂模式很少使用到,其他两种在平常开发的项目中基本上都可以看到。

作者

Somersames

发布于

2019-12-27

更新于

2021-12-05

许可协议

评论