JAVA-设计模式-2-三种工厂模式以及应用
程序猿小码 Architect

三种工厂模式以及应用

三种工厂模式,都属于创建型类型,就是关心如果创建出对象,不过 简单工厂模式 是不包含在《23种G0F》里面的,不过还是通常用的,所以这里也介绍。

简单工厂

简单工厂提供一个全局访问的实体,通过这个实体的一个静态方法,完成对象的创建

简单工厂

通过一个接口Shape(图形),限定这个类返回的类型,通常使用接口作为返回值,通过工厂内的逻辑,返回不同的对象RoundShape(圆形)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static Shape create(String name) {
if ("triangle".equals(name)) {
return new TriangleShape();
} else if ("round".equals(name)) {
return new RoundShape();
}
/**
* 违反开闭原则
else if ("square".equals(name)) {
return new SquareShape();
}
*/
throw new RuntimeException("未找到指定类型");
}

例如如上代码,全局提供一个 静态 访问点,在工厂内部完成逻辑的转换,对于外部而言,无需知道工厂内部的处理,只需要将需要的 标志 传递给工厂,让工厂 生产 对应的产品。


但是这样有一个问题:

如果后面又多了一个对象需要生产,比如一开始 Shape(图形) 工厂只生产有关于正方形、圆形 等产品,但是现在又多了一个需求,需要多生产一种类型 SquareShape 这样,我们原有的代码就在未修改的情况下,完成这一逻辑,所以就得修改代码,这就违反了 开闭原则

开闭原则

开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

说人话就是:可以通过增加实体的方式完成新逻辑的实现,但是不能修改原有代码。

这里我们原有的代码违反 开闭原则 就是因为增加一种新的逻辑的时候,需要修改工厂内部的代码,需要多加一个 if 的判断逻辑。

符合原则修改

如果每次增加一个新的类型,我们不需要修改代码,可以通过 JAVA 反射机制 通过配置文件进行增加我们新的类型,然后代码就可以在不修改的情况下,完成我们新逻辑的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ShapeFactory {
public static Shape create(String name) {
String className = XmlUtils.getShapeClass(name);
if (null == className) {
throw new RuntimeException("未找到指定的配置");
}
try {
Class classes = Class.forName(className);
return (Shape) classes.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

我们需要配置一个.XML 文件的配置解析类,来解析我们对应的逻辑,返回 class path 通过类名的方式,获取 Class 对象,调用newInstance() 获取一个实例,和调用 new 关键字完成空构造器实例化是一样的。

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<shape-item name="triangle">xyz.chaobei.impl.TriangleShape</shape-item>
<shape-item name="round">xyz.chaobei.impl.RoundShape</shape-item>
<shape-item name="square">xyz.chaobei.impl.SquareShape</shape-item>
</config>

不足点

但是这样的工厂也是有 局限性 的。比如要在创建指定的 Shape 之前,如果需要创建另外其他 对象 作为铺垫,那么,就满足不了我们的要求了。

你可能会写出这样的代码

1
2
3
4
5
6
7
8
9
10
public static Shape create(String name) {
if ("triangle".equals(name)) {
System.out.println("创建一些其他对象....")
return new TriangleShape();
} else if ("round".equals(name)) {
System.out.println("创建一些其他对象....")
return new RoundShape();
}
throw new RuntimeException("未找到指定类型");
}

工厂方法

业务需求:现需要将系统的日志打印到 文件 或者 数据库 中,并且可以通过配置文件快速的切换。

当然,如果简单工厂已经无法满足我们的要求了,因为在创建这个 Logger 对象的时候,可能需要事先创建好文件,或者连接数据库等操作,涉及需要初始化的操作和对象可能会有点多,当然,如果全部写在简单工厂 里面的话,这个类就会显得很臃肿了。

不利于后期维护。

对比简单工厂

  • 可以将创建对象的细节包含到工厂中
  • 工厂创建产品前后可以完成一些对象的初始化等。

工厂方法就是解决如上难题的,把创建对象的逻辑 封装 到对应的方法里面

image-20210818105605921

抽象工厂 LoggerFactory 定义了子类所必须实现的方法,返回具体的产品类Logger,并且提供一个访问点,直接访问产品对应的方法 writeLog()

1
2
3
4
5
6
7
8
9
public abstract class LoggerFactory {

public void writeLog() {
Logger logger = createLogger();
logger.writeLog();
}

public abstract Logger createLogger();
}

具体的子工厂 FileLoggerFactory 则是创建对应产品 FileLogger 的具体逻辑所在,一个工厂创建一个具体的对象,可能在这之前还会有一些其他的初始化操作。

1
2
3
4
5
6
7
8
9
10
11
public class FileLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {

System.out.println("FileLoggerFactory-创建文件");
Logger logger = new FileLogger();
System.out.println("FileLoggerFactory-做一些其他工作");

return logger;
}
}

产品的接口Logger 则是定义了当前产品的一些方法。

总体来说对应的产品Logger工厂AbstractFactory 是一一对应的,对比于简单工厂,就算创建当前对象的过程更加复杂,都能包含到对应的工厂内部。对外,则有更好的封装性。

同样的,我们提供 xml配置文件 反射实例化的方式,让其满足开闭原则。

如果后面业务增加,只需要增加一个对应实例工厂 以及一个新的产品 即可。

1
2
3
4
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<log-factory>xyz.chaobei.impl.FileLoggerFactory</log-factory>
</config>

不足点

但是工厂方法也有不足的地方,就是如果一旦业务对象过多,就会造成大量的 工厂类产品类

抽象工厂

抽象工厂则更好的满足 大量工厂类 导致系统臃肿的问题。如果说 工厂方法 的一个类负责一个 产品 对象的创建,则 抽象工厂 的一个类则能创建多个 产品 完成一系列(产品簇)产品的创建。

业务需求:当前系统的按钮、窗口、以及输入框需要自定义颜色配置。可以通过配置文件快速完成切换。

产品簇

一个产品簇可以包含多个子产品,类似于数据轴上的按钮、窗口、输入框组成一个产品簇。

image-20210818160201312

定义

所以,抽象工厂 把一个 产品簇 定义到一个工厂内 统一生产 解决了需要定义很多类的问题。

image-20210818163322784

对比工厂方法

假如使用 工厂方法 实现这个逻辑,则需要 3个接口6个接口实现类1个抽象定义的工厂6个对应产品的工厂

好家伙,这个类的数量有点多的离谱。。。

  • 实际的工厂类可以创建一个 产品簇 包含多个产品
  • 减少工厂类数量

不足点

如果当前产品簇需要额外加一个产品,那所有的工厂都需要重写这个定义的方法,需要修改系统现存的所有类,特别繁琐。

优缺点对比

工厂名称 优点 缺点 适用场景
简单工厂 能够配合反射满足 开闭原则,生产无参构造器简单对象 不能调用其他构造器,并且在实例化对象前后不能做任何事情 简单为数不多的对象实例化。
工厂方法 将产品的创造逻辑放到对应的工厂内部,有较好的封装性,可以在工厂内部做一些初始化操作。 随着系统产品的增多,会产生大量的 工厂类 产品类型不多,且需要实例化前后完成一些业务的场景。
抽象工厂 减少产品工厂类,一个具体工厂可以创建一个产品簇,包含多个产品 产品簇的产品数量一定,不可后期增加 产品簇产品数量一定的场景

小结

工厂模式,按需选择,合适才是硬道理。

代码示例

https://gitee.com/mrc1999/23GOF

参考内容

https://gof.quanke.name/

欢迎关注

欢迎关注微信公众号

  • 本文标题:JAVA-设计模式-2-三种工厂模式以及应用
  • 本文作者:程序猿小码
  • 创建时间:2021-08-18 15:51:32
  • 本文链接:https://keep.xpoet.cn/2021/08/18/JAVA-设计模式-3-三种工厂模式以及应用/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论