工厂模式简介(二):工厂方法模式
文章目录
我在上一篇关于工厂模式的文章中介绍了一种简单、直接的方式,可以用来封装创建的逻辑而不暴露过多细节。但一个问题是每当我们添加新的产品品类的时候,我们都需要修改工厂类,因此这种方式不是很灵活。另外,它要求工厂类依赖所有具体的产品类。尽管类注册技巧可以让我们在一定程度上消除这种依赖耦合,它们还是有一些明显的缺陷以至于这种使用方式没能被广泛使用。
这篇文章中,我们将探索一种更流行的模式来解决这些问题:工厂方法模式。
在看怎么解决这些问题前,我们先介绍一下工厂方法这种设计模式。
工厂方法模式
定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。
——GoF:《设计模式:可复用面向对象软件的基础》
工厂方法通过在父类型中使用虚拟的“占位符”指代创建步骤来指定所有的标准的和通用的行为,然后将创建的细节代理给由客户端代码指定的子类型。换句话说,工厂方法使用抽象方法将类实例的创建逻辑封装起来,因此,总是直接创建对象的 new
操作符通常需要避免使用。
工厂方法一般来说由架构性的框架来定义,由框架的使用者来实现。
上图中的例子显示了 CarFactory
工厂类接口,以及实现了 CarFactory
接口的三个具体的工厂实现类 BmwCarFactory
、VolvoCarFactory
和 TeslaCarFactory
,它们分别生产创建 BmwCar
、VolvoCar
和 TeslaCar
这几种产品类。客户端 SimpleFactoryDemo
使用具体的工厂实现类来生产一个 Car
类型,但不需要耦合具体的轿车类型。
下面是使用示例代码:
|
|
|
|
上面是 Car
和 CarFactory
接口。接下来,我们来定义具体的产品类,和生产它们的具体的产品工厂。
|
|
|
|
正如上面所看到的,具体的工厂类返回的是抽象的 Car
类型,而非具体的某一个轿车类型。
接下来,我们看客户端代码该如何使用它们。
|
|
正如上面所展示的,工厂返回抽象的 Car
类型对象后,后续就一直使用这个对象的引用。这里无需在客户端代码中耦合具体的产品类型。
为什么使用工厂方法模式?
工厂方法模式是用来解决本文开头中所提到的问题,即简单工厂模式中在有新的产品类添加到系统中来时需要修改工厂类。
什么时候我们应该使用工厂方法模式而非简单工厂模式呢?当你的代码中需要多种工厂的实现时。由于每种具体工厂类中都实现了相同的工厂接口使得生产产品的方法都返回相同的抽象类型,这可以使得客户端代码更易于替换工厂的实现。
优势和缺陷
正如上面解释的,工厂方法模式在扩展性方面拥有着很大优势,因为它在添加新类型时更为灵活。它也是软件设计原则 SOLID 中符合开闭原则一个很好的例子。
工厂方法模式也有缺点。可能你也注意到了,工厂方法模式要求有一个抽象的工厂类,并且对于每个具体产品类都要有一个具体的工厂实现类相对应。这将导致当系统中产品类型较多时类的数量会成倍增加。
现实中的例子
在 Java 中,一个描述自这篇文章的很好的例子是集合类中的 iterator()
方法。JDK 中每个集合都实现了 Iterable<E>
接口,后者定义了工厂方法 iterator()
并且返回了迭代器 Iterator<E>
这种产品类。数组 ArrayList<E>
是一种集合实现,因此它实现了 Iterable<E>
接口并且实现了它的工厂方法 iterator()
,返回了 Iterator<E>
的一个子类,即一个具体的迭代器。
下面是 Java 源码中 Iterator<E>
接口的一个简化版定义:
|
|
下面是工厂接口:
|
|
然后是 Java 源码种数组 ArrayList
的简化版定义:
|
|
正如上面看到的,它演示了一个具体的工厂实现类实现了工厂方法 iterator()
。注意在真实的 Java 源代码中,数组 ArrayList
是继承自 AbstractList
的一个类,后者是实际上实现了工厂方法模式的类。
一个对数组 ArrayList
和它的迭代器的典型使用如下所示:
|
|
正如上面演示的,数组 ArrayList
返回的迭代器 Iterator
是个接口,因此对于这个迭代器的使用方式同从链表 LinkedList
、哈希表 HashMap
、哈希集合 HashSet
或者任何其他集合类型中获取到的迭代器都没有什么两样。这正是这种模式的强大之处:你不需要了解你正在使用哪种集合,不管是哪种都会通过它的工厂方法 iterator()
提供一个迭代器 Iterator
的实例。
结论
工厂方法模式是种很有用的设计模式,适用于当系统中存在很多具体的产品类型,且这些类型可能还在增长中时。工厂方法模式通过使用一个抽象的工厂接口和其中的工厂方法,使得所有的具体工厂实现类都生产相同的产品类型,且将产品生产逻辑细节封装在了具体的工厂实现类中。
然而,这种模式的代价是它将会带来系统中类数量的提升,因为每个具体的产品类都需要一个具体的工厂类来辅助生产。
文章作者 董干
上次更新 2019-10-06