设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

  1. 引子
  2. 使用场景
  3. 常见应用场景
  4. 场景案例
  5. 常规思路
  6. 简单工厂模式
  7. 工厂方法模式
  8. 抽象工厂模式

引子

相信大家也经常在spring中见到xxxFactoty的字样,工厂模式也算是一个比较常用的设计模式,很有必要掌握

工厂模式分为三种,简单工厂,工厂方法,抽象工厂。其中简单工厂不属于23种设计模式

使用场景

不想直接new对象,如果类改变,需要改代码,如果依赖该类的很多,那将是很痛苦的事情

常见应用场景

spring中的工厂模式的继承图,之后文章中分析

img

场景案例

假如要生产电脑,比如华为,苹果电脑,如何实现业务?

常规思路

定义属性,创建方法,调用展示。

伪代码,一个类就可以实现,给我A创建A电脑,给B创建B电脑

1
2
3
4
5
if("A".equlas(type)) {
//生产A电脑
} else if("B".equlas(type)) {
//生产B电脑
}

假设在添加一个C电脑

1
2
3
4
5
6
7
if("A".equlas(type)) {
//生产A电脑
} else if("B".equlas(type)) {
//生产B电脑
} else if("C".equlas(type)) {
//生产B电脑
}

很明显,所有逻辑都写到一个类中,显然是不合适的,逐渐会形成屎山

简单工厂模式思路

将实现逻辑抽离出去,该类只做调用

先上UML类图,有个整体认知

img

抽象产品类

1
2
3
4
public interface IComputer {
//生产电脑
void method();
}

产品A实现类

1
2
3
4
5
6
public class HuaWeiIComputer implements IComputer {
@Override
public void method() {
//生产华为电脑
}
}

产品B实现类

1
2
3
4
5
6
public class AppleIComputer implements IComputer {
@Override
public void method() {
//生产苹果电脑
}
}

简单工厂

1
2
3
4
5
6
7
8
9
10
11
12
public IComputer getService(String type) {
IComputer service = null;
if ("A".equals(type)) {
service = new HuaWeiIComputer();
}
if ("B".equals(type)) {
service = new AppleIComputer();
}
//生产电脑
service.method();
return service;
}

虽然实现了一定的解耦,不将代码都放一块了,但如果添加产品C,工厂中的方法得改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public IComputer(String type) {
IComputer service = null;
if ("A".equals(type)) {
service = new HuaWeiIComputer();
}
if ("B".equals(type)) {
service = new AppleIComputer();
}
if ("C".equals(type)) {
service = new XiaoMiIComputer();
}
//生产电脑
service.method();
return service;
}

代码如上,违反了开闭原则,不适用于变化,如果产品改变(比如类名改变),也得改工厂的具体方法,于是引出了工厂方法模式

简单工厂也有存在的意义:产品稳定的情况

比如calendar的源码中也用到了简单工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}

工厂方法模式

使用场景

与简单工厂一致,用来解决简单工厂的遗留问题,其实也就是在业务类的基础上封装了一层工厂,好处在于工厂的名字是不易改变的

UML类图

img

工厂抽象类

1
2
3
public interface IComputerFactory {
IComputer getService();
}

子类实现接口

1
2
3
4
5
6
7
8
9
10
11
12
public class HuaWeiComputerFactory implements IComputerFactory {
@Override
public IComputer getService() {
return new HuaWeiIComputer();
}
}
public class AppleComputerFactory implements IComputerFactory {
@Override
public IComputer getService() {
return new AppleIComputer();
}
}

调用方

1
2
3
4
5
6
7
8
9
10
11
12
public IComputer getService(String type) {
IComputer service = null;
if ("A".equals(type)) {
HuaWeiComputerFactory aFactory = new HuaWeiComputerFactory();
service = aFactory.getService();
}
if ("B".equals(type)) {
AppleComputerFactory bFactory = new AppleComputerFactory();
service = bFactory.getService();
}
return service;
}

由于工厂名不易改变,所以调用方很稳定,但新增产品,就需要新增工厂,调用方代码还是违反了开闭原则,本质上的问题并没有得到解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public IComputer getService(String type) {
IComputer service = null;
if ("A".equals(type)) {
HuaWeiComputerFactory aFactory = new HuaWeiComputerFactory();
service = aFactory.getService();
}
if ("B".equals(type)) {
AppleComputerFactory bFactory = new AppleComputerFactory();
service = bFactory.getService();
}
if ("C".equals(type)) {
XiaoMiComputerFactory cFactory = new XiaoMiComputerFactory();
service = cFactory.getService();
}
//生产电脑
service.method();
return service;

}

优点:工厂名不易改变,稳定

缺点:不去纠结调用方的问题假如我不生产电脑了,我新增手机,对于产品簇的问题,代码会出现爆炸增长,类爆炸,所以引出抽象工厂

抽象工厂

用来解决产品簇的问题,而不是解决调用方的开闭原则问题

什么是产品簇

通俗点讲,比如苹果厂只生产苹果产品,而苹果产品包括了电脑,手机,手表等,这些一系列的产品就是产品簇

img

使用场景

华为,苹果不可能只生成一种产品,上述例子中,只生产了电脑这个产品,现在除了生产电脑,还要生产手机,那么使用原来的工厂模式的弊端就显现出来了,还得弄一个手机抽象类,具体的再去实现,那么之后再去生产别的产品呢,抽象工厂类会越来越多,实现的类也会越来越多,可能引发类爆炸的情况

比如加一个手机产品,uml类图变为这样,相当于增加一个产品,增加了6个类

img

使用抽象工厂后,现在只需要增加三个类,如果产品簇越多,效果越明显

img

抽象工厂

1
2
3
4
5
6
public interface Factory {
//生产电脑
IComputer getComputer();
//生产手机
IPhone getPhone();
}

华为工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HuaWeiFactory implements Factory{
@Override
public IComputer getComputer() {
//生产华为电脑
return new HuaWeiIComputer();
}

@Override
public IPhone getPhone() {
//生产华为手机
return new HuaWeiPhone();
}
}

苹果工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
public class AppleFactory implements Factory {
@Override
public IComputer getComputer() {
//生产苹果电脑
return new AppleIComputer();
}

@Override
public IPhone getPhone() {
//生产苹果手机
return new ApplePhone();
}
}

调用

1
2
3
4
5
//获得苹果工厂
Factory apple = new AppleFactory();
//可生产所有的苹果产品
apple.getComputer();
apple.getPhone();

优点:将工厂类的方法减少,适用于增加工厂的情况(比如增加一个小米工厂)

缺点:当产品等级发生变化时(比如在增加一个手表产品),都需要修改以前工厂产品的代码(每个工厂都需要多加一个),违反开闭原则

所以产品等级总是变化的时候,那么抽象工厂就不合适

总结

需求不易改变选择简单工厂

产品等级少选工厂方法

产品等级稳定选抽象工厂