type
Post
status
Published
date
Oct 17, 2022
slug
factory-design-pattern-of-creation-pattern
summary
tags
工厂模式
category
设计模式
icon
password
一、工厂模式概述
一般情况下,工厂模式分为三种更加细分的类型:
- 简单工厂
- 工厂方法
- 抽象工厂
不过,在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以工厂模式只被分成了工厂方法和抽象工厂两类。实际上,前面一种分类方法更加常见。
在这三种细分的工厂模式中,简单工厂、工厂方法原理比较简单,在实际的项目中也比较常用。而抽象工厂的原理稍微复杂点,在实际的项目中相对也不常用。
在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则。
如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦。
二、简单工厂模式
以下方例子展开描述:
根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig。
1、第一种实现方式
- 多个if分支判断,增加组件会改动工厂类,违反开闭原则,但不频繁,可以接收
- 是否可用多态进行优化,由于if不会太多,是可以接受的
- 对于扩展性和可读性,这样实现在不频繁发生变动的工厂类下是可以接受的
2、第二种实现方式
在 parser 可以复用的前提下,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。
- 类似单例模式和简单工厂模式的结合
三、工厂方法模式
同样是上面的例子,可以使用多态进行优化,去掉多个if分支:
- 增加 Parser 只需增加实现类即可
- 工厂方法模式比起简单工厂模式更加符合开闭原则
下方就是工厂方法模式的核心:
为了将创建各个工厂的代码进行解耦,我们需要针对这多个工厂之上建立工厂的工厂:
使用如下:
分析:
- 当我们需要添加新的规则配置解析器的时候,我们只需要创建新的 parser 类和 parser factory 类,并且在 RuleConfigParserFactoryMap 类中,将新的 parser factory 对象添加到 cachedFactories 中即可。代码的改动非常少,基本上符合开闭原则。
- 实际上,对于规则配置文件解析这个应用场景来说,工厂模式需要额外创建诸多 Factory 类,也会增加代码的复杂性,而且,每个 Factory 类只是做简单的 new 操作,功能非常单薄(只有一行代码),也没必要设计成独立的类,所以,在这个应用场景下,简单工厂模式简单好用,比工厂方法模式更加合适。
四、抽象工厂模式
在简单工厂和工厂方法中,类只有一种分类方式;如果需要两种分类方式,针对规则配置和系统配置两种进行分类,若还是使用工厂方法,我们还需要针对新的分类方式编写四种工厂类,即只要增加一种分类方式,都会对应的的增加四种工厂类,类越多越难以维护!
抽象工厂模式的应用场景比较特殊,是为了解决工厂方法多种分类方式复杂且类太多的问题而产生的,即使用抽象工厂可以方便的在一个工厂里创建多种类型的实例!
实现代码如下:
即在多种类型之上创建类型的工厂接口,让子类实现该接口重写所有的类型的方法!
类似工厂方法可以在多种实现之上再建立一个工厂的工厂,使用简单工厂的第二种实现即可!
五、依赖注入 DI 框架
当创建对象是一个“大工程”的时候,我们一般会选择使用工厂模式,来封装对象复杂的创建过程,将对象的创建和使用分离,让代码更加清晰。
- 一种是创建过程涉及复杂的 if-else 分支判断
- 另一种是对象创建需要组装多个其他类对象或者需要复杂的初始化过程。
1、工厂模式和 DI 框架有何区别
- DI 容器底层最基本的设计思路就是基于工厂模式的。DI 容器相当于一个大的工厂类,负责在程序启动的时候,根据配置(要创建哪些类对象,每个类对象的创建需要依赖哪些其他类对象)事先创建好对象。当应用程序需要使用某个类对象的时候,直接从容器中获取即可。
- DI 容器处理的是更大的对象创建工程。工厂模式中,一个工厂类只负责某个类对象或者某一组相关类对象的创建,而 DI 容器负责的是整个应用中所有类对象的创建。
- DI 容器负责的事情要比单纯的工厂模式要多。比如,它还包括配置的解析、对象生命周期的管理等。
2、DI 容器的核心功能有哪些
配置解析
工厂类要创建哪个类对象不应该是事先确定好的,我们将需要由 DI 容器来创建的类对象和创建类对象的必要信息(使用哪个构造函数以及对应的构造函数参数都是什么等等),放到配置文件中。容器读取配置文件,根据配置文件提供的信息来创建对象。
对象创建
在 DI 容器中,如果我们给每个类都对应创建一个工厂类,那项目中类的个数会成倍增加,这会增加代码的维护成本。要解决这个问题并不难。我们只需要将所有类对象的创建都放到一个工厂类中完成就可以了,比如 BeansFactory。
BeansFactory 使用 “反射”这种机制,在程序运行的过程中,动态地加载类、创建对象,不会由于需要创建的的对象太多代码线性增加。
对象生命周期管理
简单工厂模式有两种实现方式,一种是每次都返回新创建的对象,另一种是每次都返回同一个事先创建好的对象,也就是所谓的单例对象。
在 Spring 框架中,我们可以通过配置 scope 属性,来区分这两种不同类型的对象:
- scope=prototype 表示返回新创建的对象
- scope=singleton 表示返回单例对象
除此之外,我们还可以配置对象是否支持懒加载,如 lazy-init=true/false。
不仅如此,我们还可以配置对象的 init-method 和 destroy-method 方法等。
六、实现一个简单的 DI 容器
核心逻辑只需要包括这样两个部分:
- 配置文件解析
- 根据配置文件通过“反射”创建对象
1、最小原型设计
配置文件定义对象创建信息:
配置文件解析创建和获取 Bean:
2、ApplicationContext 设计
ClassPathXmlApplicationContext 负责组装 BeansFactory 和 BeanConfigParser 两个类,串联执行流程:从 classpath 中加载 XML 格式的配置文件,通过 BeanConfigParser 解析为统一的 BeanDefinition 格式,然后,BeansFactory 根据 BeanDefinition 来创建对象。
3、配置文件解析
BeanConfigParser 接口和 XmlBeanConfigParser 实现类,负责将配置文件解析为 BeanDefinition 结构,以便 BeansFactory 根据这个结构来创建对象。
配置文件的解析比较繁琐,略过!
4、核心工厂类设计
BeansFactory 根据 配置文件解析的结果 List<BeanDefinition> 创建对象!
- 作者:NotionNext
- 链接:https://tangly1024.com/article/factory-design-pattern-of-creation-pattern
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

