工厂方法(Factory Method)

Intent

定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。

Class Diagram

在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。

下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。

设计模式 - 工厂方法 - 图1

Implementation

  1. public abstract class Factory {
  2. abstract public Product factoryMethod();
  3. public void doSomething() {
  4. Product product = factoryMethod();
  5. // do something with the product
  6. }
  7. }
  1. public class ConcreteFactory extends Factory {
  2. public Product factoryMethod() {
  3. return new ConcreteProduct();
  4. }
  5. }
  1. public class ConcreteFactory1 extends Factory {
  2. public Product factoryMethod() {
  3. return new ConcreteProduct1();
  4. }
  5. }
  1. public class ConcreteFactory2 extends Factory {
  2. public Product factoryMethod() {
  3. return new ConcreteProduct2();
  4. }
  5. }

工厂方法模式总结

工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。

工厂方法模式的主要优点

  • 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
  • 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
  • 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合”开闭原则”。

    工厂方法模式的主要缺点

  • 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。

  • 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

    适用场景

  • 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。

  • 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

    工厂方法模式的典型应用及源码分析

    Java集合接口 Collection 中的工厂方法模式

    Collection 中的 iterator 方法如下:
    1. public interface Collection<E> extends Iterable<E> {
    2. Iterator<E> iterator();
    3. // ...省略
    4. }
    关于 iterator 方法的介绍: Java的迭代器只在Collection中有,而Map没有迭代器,它有不同的迭代方法;
    迭代器的终极目标:就是用统一的方法来迭代不同类型的集合!可能由于不同集合的内部数据结构不尽相同,如果要自己纯手工迭代的话相互之间会有很大的差别,而迭代器的作用就是统一的方法对不同的集合进行迭代,而在迭代器底层隐藏不同集合之间的差异,从而为迭代提供最大的方便 使用用迭代器迭代的步骤: i. 第一步肯定是先获取集合的迭代器:调用集合的iterator方法就能获得,IteratorCollection.iterator(); ii. 使用迭代器的hasNext、next往下迭代
    Iterator的常用方法:boolean hasNext():是否还有下一个元素; Object next():取出下一个元素并返回; void remove(); :从容器中删除当前元素,直接会改变容器中的数据
    查看该接口的实现类,可以看到是非常的多
    Collection接口的实现类(部分)
    我们仅看其中一个实现类 java.util.ArrayList,看其对 iterator 方法的实现
    1. public Iterator<E> iterator() {
    2. return new Itr();
    3. }
    4. /**
    5. * An optimized version of AbstractList.Itr
    6. */
    7. private class Itr implements Iterator<E> {
    8. int cursor;
    9. // index of next element to return
    10. int lastRet = -1;
    11. // index of last element returned; -1 if no such
    12. int expectedModCount = modCount;
    13. Itr() {}
    14. public boolean hasNext() {
    15. return cursor != size;
    16. }
    17. @SuppressWarnings("unchecked")
    18. public E next() {
    19. // ...省略...
    20. }
    21. public void remove() {
    22. // ...省略...
    23. }
    24. @Override
    25. @SuppressWarnings("unchecked")
    26. public void forEachRemaining(Consumer<? super E> consumer) {
    27. // ...省略...
    28. }
    29. final void checkForComodification() {
    30. // ...省略...
    31. }
    32. }
    Itr 类实现了 iterator 接口,iterator 接口正是 Collection 接口中 iterator 方法的返回类型,其代码如下:
    1. public interface Iterator<E> {
    2. boolean hasNext();
    3. E next();
    4. default void remove() {
    5. throw new UnsupportedOperationException("remove");
    6. }
    7. default void forEachRemaining(Consumer<? super E> action) {
    8. Objects.requireNonNull(action);
    9. while (hasNext())
    10. action.accept(next());
    11. }
    12. }
    由此可见,Collection 接口扮演了抽象工厂角色,工厂方法为 iterator(),Collection 的实现类譬如 ArrayList 扮演了具体工厂角色,而抽象产品为 Iterator 接口,具体产品为 Itr 类