简介

分为三种:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

工厂模式主要是隐藏掉类的创建入口,使得一个较为复杂的类的创建,转为了由工厂类提供。比如某种水果罐头的类,其配料表较为复杂,如果在每一处都使用new的方式去创建,那么如果出现需要更改,则会涉及到多处代码的改动,则如果把类的创建交给工厂来提供,那么对于业务的代码耦合性上会得到大大的降低。但是也要注意不要滥用工厂模式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
使用场景
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
3、设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

工厂模式是一种设计模式上的封装。

简单工厂

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

  1. public class RuleConfigParserFactory {
  2. private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
  3. static {
  4. cachedParsers.put("json", new JsonRuleConfigParser());
  5. cachedParsers.put("xml", new XmlRuleConfigParser());
  6. cachedParsers.put("yaml", new YamlRuleConfigParser());
  7. cachedParsers.put("properties", new PropertiesRuleConfigParser());
  8. }
  9. public static IRuleConfigParser createParser(String configFormat) {
  10. if (configFormat == null || configFormat.isEmpty()) {
  11. return null;//返回null还是IllegalArgumentException全凭你自己说了算
  12. }
  13. IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
  14. return parser;
  15. }
  16. }

一段极客时间上的范例,可以从中提取出多个关键点:

  • 工厂类一般不是只对一个类的生产,而是具有相似能力的一类产品的生产
  • 工厂类在解耦对类的创建时,会引入接口的多态,将相关功能的产品的同类功能点抽象提取出来,继承接口实现对应的功能方法,这样在外部调用时,可以面向接口编程,拓展性,解耦能力更强
  • 工厂可以根据传入参数的不同,实现生产不同的产品(即创造不同的对象实例)
  • 简单工厂不会继承接口,这样只会有一个工厂,该工厂内负责生产多种产品

工厂方法

将工厂类进行进一步的抽象,提取出来工厂统一的行为,这样继承此接口编写多个工厂类,这样生成每个对象都在工厂类中进行创建。
为了解决在对外调用的工厂类中的判断使用哪个工厂来构造,这儿可以再使用一个简单工厂,用来生产工厂,即如下:
C1类、C2类、C3类:真正想要用的类
A工厂:对外的工厂类,比如main中根据A工厂获取要使用的真实类,即获取C1、C2、C3
B工厂、C工厂、D工厂:用来生产C1类的工厂、C2类的工厂、C3类的工厂
E工厂:简单工厂,用来生产B、C、D工厂的工厂

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

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

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

抽象工厂

减少工厂类的数量。

我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。