目录

[TOC]

参考

文档

廖雪峰的官方网站:https://www.liaoxuefeng.com/wiki/1252599548343744/1281319170474017

博客文档:

代码

https://gitee.com/xiao-i-fei/xiaofei-design-mode

https://github.com/xiao-i-fei/xiaofei-design-mode

什么是工厂模式

工厂设计模式的目的是使得创建对象和使用对象是分离的,调用者只管获取对象获取对象,无需知道对象是怎么创建的,工厂模式是一种创建型设计模式,它用于解耦对象的创建和使用。通常情况下,我们创建对象时需要使用new操作符,但是使用new操作符创建对象会使代码具有耦合性。工厂模式通过提供一个公共的接口,使得我们可以在不暴露对象创建逻辑的情况下创建对象。

工厂模式的分类

工厂模式分为三种类型:

  • 简单工厂
  • 方法工厂
  • 抽象工厂

其本质就是对获取对象过程的抽象

应用场景

应用实例

你需要一辆汽车,可以直接从工厂里提货,而不用关心它具体是怎么实现的

Hibernate换数据库只需要换方言和驱动就可以

使用场景

日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。

设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口

工厂模式实现

传统模式

在介绍工厂模式之前先来看看传统模式,以卖包子为例,如下:

1
2
3
4
5
6
7
8
9
10
11
//简单的制作流程
public BaoZi createBaoZi() {
BaoZi baozi = new BaoZiImpl();
//准备材料
baozi.prepare();
//制作包子
baozi.make();
//蒸包子
baozi.braise();
return baozi;
}

包子肯定有很多种类吧,那我们可以直接在上述代码中添加根据包子的种类生成不同类型的对象

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
/**
* 包子肯定有不同的馅:酸菜、豆沙、猪肉,那么他的材料、售价等方式也不同
* 我们可以直接在上述代码中,添加根据包子的不同种类生成不同的对象。
*/
public BaoZi createBaoZi(String type) {
BaoZi baoZi = null;
switch (type) {
case "suancai":
baoZi = new SuanCaiBaoZi();
break;
case "dousha":
baoZi = new DouShaBaoZi();
break;
case "pork":
baoZi = new PorkBaoZi();
break;
default:
throw new IllegalArgumentException("Invalid BaoZi Type");
}
//准备材料
baoZi.prepare();
//制作包子
baoZi.make();
//蒸包子
baoZi.braise();
return baoZi;
}

image-20230531172315922

测试:

1
2
3
4
5
6
7
//1.传统模式
@Test
void traditonal(){
SaleBaoZi saleBaoZi=new SaleBaoZi();
//以猪肉包为例
saleBaoZi.createBaoZi("pork");
}

简单工厂模式

简单工程根据客户端的需求创建具体的实例,这种模式对调用者隐藏了实例创建的过程,也使得创建过程更加容易维护。

还是以卖包子为例,简单工厂模式实现如下:

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
/**
* 2.简单工厂方法:希望能够创建一个对象,但创建过程比较复杂,希望对外隐藏这些细节
*/
public class SimpleFactory {

public static BaoZi createBaoZi(String type){

BaoZi baoZi=null;
switch (type){
case "suancai":
baoZi=new SuanCaiBaoZi("酸菜包");
break;
case "dousha":
baoZi=new DouShaBaoZi("豆沙包");
break;
case "lajioa":
baoZi=new PorkBaoZi("猪肉包");
case "beef":
//老板拓展业务了,新加了一个牛肉包类型的包子,那对于简单工厂模式而言,
//于是就得修改源代码,那么就违反了ocp原则,假如新增100个?
baoZi=new BeefBaoZi("牛肉包");
break;
default:
throw new IllegalArgumentException("Invalid BaoZi Type");
}

return baoZi;
}
}

此时类图如下:

image-20230531172545428

测试:

1
2
3
4
5
6
7
8
9
//2.简单工厂模式
@Test
void simpleFactory(){
//以猪肉包为例
BaoZi pork = SimpleFactory.createBaoZi("pork");
pork.prepare();
pork.make();
pork.braise();
}

相比传统模式,从类图上就可以看出来,在sale和baozi中间又加了一层

通过封装SimpleFactory这个类,我们将sale和baozi进行了解耦合。

方法工厂模式

简单工厂模式下,如果老板拓展业务了,加了一个牛肉种类的包子,就得在源码基础上修改,那么这就违背了开闭原则(ocp),即对扩展开放,对修改关闭。于是,为了解决这个问题,就又了工厂方法模式。

工厂方法模式是一种更加抽象的工厂模式,它将工厂的职责抽象为接口,由具体的工厂实现创建具体的对象。工厂方法模式弱化了工厂的实现,使得每个工厂只负责一个产品的创建。

抽象工厂MeAbStractFactory:

1
2
3
public interface MeAbstractFactory {
BaoZi createBaoZi();
}

DouShaFactory:

1
2
3
4
5
6
public class DouShaFactory implements MeAbstractFactory {
@Override
public BaoZi createBaoZi() {
return new DouShaBaoZi("豆沙包");
}
}

SuanCaiFactory:

1
2
3
4
5
6
public class SuanCaiFactory implements MeAbstractFactory {
@Override
public BaoZi createBaoZi() {
return new PorkBaoZi("酸菜包");
}
}

LaJiaoFactory:

1
2
3
4
5
6
public class SuanCaiFactory implements MeAbstractFactory {
@Override
public BaoZi createBaoZi() {
return new PorkBaoZi("辣椒包");
}
}

此时的类图如下:

image-20230531173424995

测试:

1
2
3
4
5
6
7
8
@Test
void methodFactory(){
MeAbstractFactory factory=new PorkFactory();
BaoZi pork = factory.createBaoZi();
pork.prepare();
pork.make();
pork.braise();
}

之前的SimpleFactory在createBaoZi中直接就new出来了,但在方法工厂中,我们将createBaoZi这个动作推迟到了MeAbStactFactory的子类(XXFactory)中才完成。好处就是,比如后期要卖个羊肉包,我们直接编写个羊肉包类,然后实现MeAbstractFactory类就,实现它自己的功能,这样完全不用修改原来的代码了,也就解决了违反OCP原则的问题。

抽象工厂

抽象工厂模式是基于工厂方法模式的基础上进行的。在这种模式中,每一个工厂不再只负责一个产品的创建,而是负责一组产品的创建。抽象工厂模式将每个产品组都提取为一个接口,每个工厂都负责一个产品组。

假如老板的生意做大了,在北京开了个分店,并且不止卖包子,还卖蛋糕,那么该怎么拓展呢,很简单,只需要在抽象工厂类中新增创建蛋糕的抽象方法就行,如下:

AbstractFactory:

1
2
3
4
5
6
public interface AbstractFactory {
//制作包子
BaoZi createBaoZi(String type);
//制作蛋糕
Cake createCake(String type);
}

BJFactory:

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
 //北京分店
public class BJFactory implements AbstractFactory{
@Override
public BaoZi createBaoZi(String type) {
BaoZi baoZi=null;
switch (type){
case "suancai":
baoZi=new BJSuanCaiBao("北京酸菜包");
break;
case "lajioa":
baoZi=new BJLaJiaoBao("北京辣椒包");
default:
break;
}
return baoZi;
}

@Override
public Cake createCake(String type) {
Cake cake=null;
switch (type){
case "apple":
cake=new BJAppleCake("北京苹果蛋糕");
break;
case "pear":
cake=new BJPearCake("北京梨味蛋糕");
default:
break;
}
return cake;
}
}

可以看到,抽象工厂仅仅是在工厂方法模式下新增了一些接口,只是工厂模式的一个拓展,当抽象工厂只有一个产品体系的话就会退化成工厂模式,所以两者本质上没有太大的区别。

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
void abstractFactory(){
AbstractFactory factory=new BJFactory();
Cake apple = factory.createCake("apple");
BaoZi pork = factory.createBaoZi("pork");

apple.prepare();
apple.make();
apple.bake();
apple.sale();

pork.prepare();
pork.make();
pork.braise();
pork.sale();
}

总结

简单工厂模式

  • 优点:

    • 简单工厂模式实现简单,易于理解和使用;
    • 可以对对象的创建进行集中管理,客户端和具体实现解耦。
  • 缺点:
    • 工厂类负责创建所有对象,如果需要添加新类型的产品,则需要修改工厂类的代码,这违反了开闭原则;
    • 工厂类职责过重,导致单一职责原则被破坏。
  • 适用场景:
    • 工厂类负责创建的对象较少,客户端不需要知道对象的创建过程;
    • 客户端需要根据传递的参数来获取对应的对象。

方法工厂模式

  • 优点:

    • 方法工厂模式具有良好的可扩展性,如果需要添加新类型的产品,只需要添加对应的工厂方法即可;
    • 与简单工厂模式相比,方法工厂模式更符合开闭原则和单一职责原则。
  • 缺点:
    • 需要客户端自行选择使用哪个工厂方法,不能像简单工厂模式那样直接传参获取对应对象,因此对客户端的编写有一定要求。
  • 适用场景:
    • 应用中需要创建的对象较少,但是需要具备良好的可扩展性;
    • 客户端可以自行选择创建哪种对象。

抽象工厂

  • 优点:

    • 抽象工厂模式可以创建多个产品族的产品,这些产品之间有相互依赖或约束关系,有助于保持系统的一致性和稳定性;
    • 客户端与具体产品解耦,通过产品族的方式进行管理。
  • 缺点:
    • 抽象工厂模式增加了系统的抽象性和理解难度,不易于理解和修改;
    • 新增产品族时需要修改工厂接口、工厂实现类和产品类,增加了系统的复杂性。
  • 适用场景:
    • 系统需要一系列相互依赖或约束的产品;
    • 客户端不需要知道具体产品的创建过程,只需要知道产品族即可。