9

JAVA-设计模式-2-三种工厂模式以及应用

 3 years ago
source link: https://blogs.chaobei.xyz/2021/08/18/JAVA-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-3-%E4%B8%89%E7%A7%8D%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%E4%BB%A5%E5%8F%8A%E5%BA%94%E7%94%A8/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

三种工厂模式以及应用

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

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

简单工厂

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

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 反射机制 通过配置文件进行增加我们新的类型,然后代码就可以在不修改的情况下,完成我们新逻辑的实现。

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 关键字完成空构造器实例化是一样的。

<?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 之前,如果需要创建另外其他 对象 作为铺垫,那么,就满足不了我们的要求了。

你可能会写出这样的代码

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()

public abstract class LoggerFactory {

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

public abstract Logger createLogger();
}

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

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配置文件 反射实例化的方式,让其满足开闭原则。

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

<?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/

欢迎关注微信公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK