设计模式-创建型-工厂模式

般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂方法和抽象工厂。不过,
在 GoF 的《设计模式》一书中,它将简单工厂模式看作是工厂方法模式的一种特例,所以
工厂模式只被分成了工厂方法和抽象工厂两类。

简单工厂(Simple Factory)

我们根据配置文件的后缀(json、xml、yaml、properties),选择不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),将存储在文件中的配置解析成内存对象 RuleConfig。

第一种实现方式

大部分工厂类都是以“Factory”这个单词结尾的,但也不是必须的,比如 Java 中的 DateFormat、Calender。除此之外,工厂类中创建对象的方法一般都是 create 开头,比如代码中的 createParser(),但有的也命名为 getInstance()、createInstance()、newInstance(),有的甚至命名为 valueOf()(比如 Java String 类的 valueOf() 函数)等等,这个我们根据具体的场景和习惯来命名就好。

第二种实现方式

在上面的代码实现中,我们每次调用 RuleConfigParserFactory 的 createParser() 的时候,都要创建一个新的 parser。实际上,如果 parser 可以复用,为了节省内存和对象创建的时间,我们可以将 parser 事先创建好缓存起来。当调用 createParser() 函数的时候,我
们从缓存中取出 parser 对象直接使用。

工厂方法(Factory Method)

简单工厂中,每一种解析器都是直接使用 new 创建的,没有统一的规范,我们可以为其创建一个接口,使其统一规范起来。

使用的时候,可以参考简单工厂的第二种方式,不需要每次都创建新的工厂类对象,可以使用 Map 将其缓存起来。

如何选择

我们前面提到,之所以将某个代码块剥离出来,独立为函数或者类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护。但是,如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的函数或者类。

基于这个设计思想,当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,我们推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的
创建逻辑都放到一个工厂类中,会导致这个工厂类变得很复杂。

除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同的对象。如果我们使用简单工厂模式来实现,就只能选择第一种包含 if 分支逻辑的实现方式。如果我们还想避免烦人的 if-else 分支逻辑,这个时候,我们就推荐使用工厂方法模式

抽象工厂(Abstract Factory)

抽象工厂模式的应用场景比较特殊,没有前两种常用,所以不是学习的重点。

在简单工厂和工厂方法中,类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。但是,如果类有两种分类方式,比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置
还是 System 系统配置)来分类,那就会对应下面这 8 个 parser 类。

1
2
3
4
5
6
7
8
9
10
11
针对规则配置的解析器:基于接口 IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser

针对系统配置的解析器:基于接口 ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser

针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。如果我们未来还需要增加针对业务配置的解析器(比如 IBizConfigParser),那就要再对应地增加 4 个工厂类。而我们知道,过多的类也会让系统难维护。这个问题该怎么解决呢?

抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser对象。这样就可以有效地减少工厂类的个数。具体的代码实现如下所示:

以上就是类的分类不止一种时,采用抽象工厂的实现。

总结

根据不同的类型需要创建不同类型的类,那么可以使用简单工厂,而且创建的对象可以使用同一个的话,我们可以借助 Map 将对象进行缓存,而不是每次都创建一个。这就是简单工厂。

不同类型的工厂(JsonRuleConfigParserFactory,XmlRuleConfigParserFactory)使用一个接口(IRuleConfigParserFactory)来规范,并且提供创建对象方法,那么这种模式就属于工厂方法。

当类不仅仅只有一种分类方式的时候,例如 Json 解析可以分为 JsonRuleConfigParser 和 JsonSystemConfigParser 的时候,我们可以使用一个接口来统一规则,子类实现的时候就会创建不同分类的对象。这就属于抽象工厂。