factory_method

第二章 工厂模式(Factory Method Pattern)

1 概要

工厂模式(Factory Method Pattern)是一种创建模式(Creational Design Patterns)。在面向对象程序设计中,对象是由new操作符创建出来的。在小型项目中,直接使用new操作符创建对象并不会增加代码维护的难度。然而,随着项目规模增大,在更多的类加入项目后,管理对象的创建会变得非常复杂。因此,开发人员设计了工厂模式。在工厂模式下,开发人员通过统一调用工厂类的方法来创建对象,增强了项目代码的可维护性。另一方面,工厂模式将对象的创建逻辑和使用逻辑分离开,对象创建的逻辑被"推迟"到子类中实现,帮助开发人员更加清晰的组织和维护代码。

2 工厂模式的结构

在工厂模式中,有四个参与方。

  1. ProductBase类代表的是工厂模式创建出的对象的接口。
  2. ConcreteProduct类是工厂模式创建出的真实对象的类。
  3. FactoryBase是工厂类接口。开发人员使用FactoryBase定义的方法创建Product对象。
  4. ConcreteFactory是具体的工厂实现类。ConcreteFactory是真正创建Product对象的类。

图一 工厂模式结构

图一 工厂模式结构。

3 工厂模式示例

我们下面将使用笔记本电脑为例讲解工厂模式的使用方法。首先,我们定义了笔记本电脑类Laptop。它有一个方法installOS(),表示在笔记本上安装操作系统(Operating System)。

public abstract class Laptop {
    public void installOS();
}

然后,我们定义三个具体的笔记本类,它们分别表示运行Linux、macOS、和Windows的三类笔记本电脑。

public class LinuxLaptop extends Laptop {
    @Override 
    public void installOS() {
        System.out.println("Install Linux ...");
    }
}

public class MacBook extends Laptop {
    @Override 
    public void installOS() {
        System.out.println("Install macOS ...");
    }
}

public class WindowsLaptop extends Laptop {
    @Override 
    public void installOS() {
        System.out.println("Install Windows ...");
    }
}

在第三步中,我们再定义笔记本工厂类。LaptopFactoryBase为工厂抽象类。方法make()用于创建一个Laptop对象。

public abstract class LaptopFactoryBase {
    public abstract Laptop make(String type);
}

LaptopFactory为具体的工厂类,继承自LaptopFactoryBase。在LaptopFactory::make()方法中,根据传入的参数type的值,分别构造LinuxLaptop对象,MacBook对象和WindowsLaptop对象。在返回之前,调用installOS()方法安装操作系统。

public class LaptopFactory extends LaptopFactoryBase {
    @Override 
    public Laptop make(String type) {
        Laptop laptop = null;
        switch (type.toLowerCase()) {
            case "linux":
                laptop = new LinuxLaptop();
                break;
            case "macbook":
                laptop = new MacBook();
                break;
            case "windows":
                laptop = new WindowsLaptop();
                break;
            default:
                throw new IllegalArgumentException("Unknown Laptop Type.");
        }

        laptop.installOS();
        return laptop;
    }
}

当开发人员使用工厂创建Laptop对象时,可先创建LaptopFactory对象,然后将需要创建的笔记本对象参数传入到工厂类的make()方法中。

public class LaptopFactoryExample {
    public static void main(String[] args) {
        LaptopFactoryBase factory = new LaptopFactory();
        Laptop linuxLaptop = factory.make("linux");
        ...
    }
}

4 应用举例

工厂模式广泛的应用于各种项目之中。工厂模式有许多种表现形式,但是,它的主要目的是创建对象。在Java标准库中,有许多使用工厂模式的地方,我们例举两处如下。

4.1 Integer.valueOf()方法

第一处是我们常用的类Integer。Integer类的方法valueOf()应用了工厂模式。当valueOf()方法收到字符串参数时,它会解析该字符串,并将其转换成一个整数。当接收到一个整数时,如果这个整数在IntegerCache.low和IntegerCache.high的范围内,valueOf()方法会使用事先创建好的缓存对象。如果在其范围以外,则会创建一个新的Integer对象。

从这个例子可以看出,Integer类运用了工厂模式,"隐藏"了对象创建过程的细节。在使用时,开发人员无需知晓Integer对象是如何创建出来的。Java标准库中Long::valueOf(),Float::valueOf()和Double::valueOf()都运用了相似的工厂模式。

// java.lang.Integer.java in OpenJDK 15
package java.lang;

public final class Integer extends Number implements Comparable<Integer> {
    ...
    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }

    public static Integer valueOf(String s, int radix) throws NumberFormatException {
        // 解析字符串s,并创建相应的Integer对象。
        return Integer.valueOf(parseInt(s, radix)); 
    }

    public static Integer valueOf(int i) {
        // 如果在[IntegerCache.low, IntegerCache.high]之间,则使用缓存对象,
        // 否则创建新的Integer对象
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    ...
}

4.2 NumberFormat.getInstance()方法

在Java标准库的java.text包中,NumberFormat也在getInstance()方法中运用了工厂模式。实际上,NumberFormat类的get*Instance()方法也以相似的方式创建NumberFormat对象。

NumberFormat的功能是格式化整数。它能够将整数转换成多国语言的字符串形式。getInstance()方法则隐藏了生成相应的NumberFormat对象的细节。

// java.text.NumberFormat.java in OpenJDK 15
package java.text;

public abstract class NumberFormat extends Format {
    ...
    public final static NumberFormat getInstance() {
        return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
    }

    public static NumberFormat getInstance(Locale inLocale) {
        return getInstance(inLocale, NUMBERSTYLE);
    }

    public final static NumberFormat getNumberInstance() {
        return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
    }

    public static NumberFormat getNumberInstance(Locale inLocale) {
        return getInstance(inLocale, NUMBERSTYLE);
    }
    ...
}

从我们的经验来看,当在源代码中发现静态方法,且命名为getInstance()时,这些方法很有可能使用了工厂模式。在Java标准库中,类似的工厂模式还应用在以下的类中。有兴趣的读者可自行查看其源代码和实现细节。

  1. java.lang.Class.newInstance()
  2. java.util.Calendar.getInstance()
  3. java.util.ResourceBundle.getBundle()
  4. java.nio.charset.Charset.forName()
  5. java.net.EnumSet.of()

5 小结

本章介绍了工厂模式的结构和使用方法。工厂模式的优点是它将创建对象的逻辑与业务逻辑分离开来。开发人员在开发业务逻辑时,无需考虑如何创建这个对象。当对象的创建过程较为复杂时,工厂模式能够有效的提高代码可读性和可维护性。

 

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.