abstract_factory

第三章 抽象工厂模式(Abstract Factory Pattern)

1 概要

抽象工厂模式(Abstract Factory Pattern)是一种创建模式(Creational Design Patterns)。抽象工厂模式可被用于创建一组不同类型、但相互协作的对象。通过使用抽象工厂模式,开发人员在开发业务逻辑时,无需考虑创建对象的逻辑,也无需考虑创建出来的、不同类型对象之间是否匹配。当对象创建的逻辑发生变化时,不影响对象使用的逻辑。抽象工厂模式的工作方式与工厂模式的工作方式十分相似;它们都用于创建对象。然而,抽象工厂模式在工厂模式之上又添加了一层抽象层。抽象工厂模式与工厂模式的区别如下:

  1. 抽象工厂模式包装的是创建工厂的过程;而工厂模式包装的是创建对象的过程。因此,抽象工厂模式常常被称为创建工厂的工厂(factory of factories)。
  2. 从用户的角度来看,抽象工厂可用于创建不同类型的对象;而工厂模式仅创建同一类型的对象(所有对象都继承自同一个类或者实现同一个接口)。
  3. 从实现的角度来看,工厂模式使用继承关系,具体的工厂类继承自抽象工厂类;而抽象工厂模式使用组合模式(Composition)。

2 抽象工厂模式的结构

在抽象工厂模式中,有五个参与方。

  1. AbstractProduct是抽象工厂模式创建出的对象接口。在抽象工厂模式下,一般存在有多个AbstractProduct。
  2. ConcreteProduct是抽象工厂模式创建出的具体对象类。
  3. AbstractFactory是抽象工厂类接口。开发人员使用AbstractFactory定义的方法创建Product类。
  4. ConcreteFactory是具体工厂的实现类。ConcreteFactory是真正创建Product对象的类。
  5. Client使用AbstractFactory接口创建Product对象。

图一 抽象工厂模式结构图

图一 抽象工厂模式结构图

3 抽象工厂模式示例

我们将使用配置笔记本电脑(Laptop)的例子来介绍抽象工厂模式的使用方法。在例子中,我们只关心笔记本电脑的处理器(Processor)和操作系统(Operating System)的配置。

我们首先定义Processor接口,表示处理器。并且,我们还定义了两个子类IntelProcessor和M1Processor,用来表示英特尔(Intel)处理器和苹果自研的M1处理器。成员方法install()表示在给定的Laptop对象上安装处理器。在本例中,我们假设苹果的MacBook只使用苹果自研的M1处理器。

interface Processor {
    void install(Laptop laptop);
}

public class IntelProcessor extends Processor {
    @Override 
    public void install(Laptop laptop) {
        System.out.println("Install an Intel processor ...");
    }
}

public class M1Processor extends Processor {
    @Override 
    public void install(Laptop laptop) {
        System.out.println("Install an M1 processor ...");
    }
}

然后,我们再定义操作系统接口OperatingSystem,和两个实现类Linux和MacOS。成员方法install()表示在给定的Laptop对象上安装操作系统。

interface OperatingSystem {
    void install(Laptop laptop);
}

public class Linux extends OperatingSystem {
    @Override 
    public void install(Laptop laptop) {
        System.out.println("Install Linux ...");
    }
}

public class MacOS extends OperatingSystem {
    @Override 
    public void install(Laptop laptop) {
        System.out.println("Install macOS ...");
    }
}

Laptop类包含Processor成员变量和OperatingSystem成员变量。构造函数接受Processor和OperatingSystem两个参数。

public class Laptop {
    protected Processor processor = null;
    protected OperatingSystem os = null;

    public Laptop (Processor processor, OperatingSystem os) {
        this.processor = processor;
        this.os = os;
    }
}

我们再创造两个接口ProcessorFactory和OperatingSystemFactory,用于创建笔记本的两个部件。这是一个工厂模式的应用。它们分别定义了一个方法create(),用于创建处理器Processor或者操作系统OperatingSystem对象。IntelProcessorFactory和M1ProcessorFactory实现了ProcessorFactory接口,用于创建Intel处理器对象和M1处理器对象。LinuxFactory和MacOSFactory实现了OperatingSystemFactory接口,用于创建Linux操作系统对象和MacOS操作系统对象。

interface ProcessorFactory {
    Processor create();
}

public class IntelProcessorFactory implements ProcessorFactory {
    @Override 
    public Processor create() {
        return new IntelProcessor();
    }
}

public class M1ProcessorFactory implements ProcessorFactory {
    @Override 
    public Processor create() {
        return new M1Processor();
    }
}

interface OperatingSystemFactory {
    OperatingSystem create();
}

public class LinuxFactory implements OperatingSystemFactory {
    @Override 
    public OperatingSystem create() {
        return new Linux();
    }
}

public class MacOSFactory implements OperatingSystemFactory {
    @Override 
    public OperatingSystem create() {
        return new MacOs();
    }
}

最后,我们使用LaptopFactory类创建Laptop对象。LaptopFactory使用了抽象工厂模式,它的newInstance()方法接收一个参数,用于表明待创建的Laptop对象的种类。它的create()方法用于创建并组装笔记本对象。两个具体实现类LinuxLaptopFactory和MacbookFactory分别用于创建LinuxLaptop和Macbook对象。

public abstract class LaptopFactory {
    private ProcessorFactory processorFactory = null;
    private OperatingSystemFactory operatingSystemFactory = null;
    
    public LaptopFactory(ProcessorFactory pf, OperatingSystemFactory osf) {
        this.processorFactory = pf;
        this.operatingSystemFactory = osf;
    }

    public Laptop create() {
        Processor processor = this.processorFactory.create();
        OperatingSystem os = this.operatingSystemFactory.create();
        Laptop laptop = new Laptop(this.processor, this.os);
        this.processor.install(laptop);
        this.os.install(laptop);
        return laptop;
    }
    
    public static LaptopFactory newInstance(String type) throws IllegalArgumentException {
        if ("Linux".equals(type)) {
            return new LinuxLaptopFactory();
        } else if ("MacOS".equals(type)) {
            return new MacbookFactory();
        } else {
            throw new IllegalArgumentException();
        }
    }
}

public class LinuxLaptopFactory extends LaptopFactory {
    public LinuxLaptop() {
        super(new IntelProcessorFactory(), new LinuxFactory());
    }
}

public class MacbookFactory extends LaptopFactory {
    public Macbook() {
        super(new M1ProcessorFactory(), new MacOSFactory());
    }
}

因此,在客户端处,我们可以使用如下代码创建指定的Laptop对象。

public class LaptopCreationClient {
    public static void main(String[] args) {
        LaptopFactory linuxLaptopfactory = LaptopFactory.newInstance("Linux");
        Laptop linuxLaptop = linuxLaptopfactory.create();

        LaptopFactory macOSFactory = LaptopFactory.newInstance("MacOS");
        Laptop macOSLaptop = macOSFactory.create();
    }
}

这个例子展现了一个抽象工厂模式与工厂模式的主要区别。抽象工厂模式能够将创造出来的对象结合在一起。假如我们需要创建一个LinuxLaptop的对象,如果我们只使用工厂模式创建处理器和操作系统的话,开发人员还需要了解LinuxLaptop对象使用的是Intel处理器对象和Linux操作系统对象。如果这两项搭配错误的话,代码逻辑将是错误的。所以,如果我们使用抽象工厂模式,将部件之间的搭配封装在新增加的抽象层中,以确保创建出的LinuxLaptop对象使用的是Intel处理器对象和Linux操作系统对象,而创建出的Macbook对象使用的是M1处理器对象和MacOS操作系统对象。在对象的使用过程中,开发人员无需担心部件之间的搭配问题。

而且,当我们需要新增一款WindowsLaptop对象时,我们可以新增一个WindowsLaptopFactory类,实现LaptopFactory接口。开发人员无需修改使用端(LaptopCreationClient类)的代码。

4 应用举例

抽象工厂模式广泛的应用于各种项目之中,在Java的标准库中也有不少应用。我们在本节中将介绍javax.xml.parsers.DocumentBuilderFactory类中使用的抽象工厂模式。其他的例子还有javax.xml.transform.TransformerFactory和javax.xml.xpath.XPathFactory。读者可自行阅读其源代码。

在DocumentBuilderFactory类中,静态方法newInstance()创建一个默认的DocumentBuilderFactory对象。该对象的newDocumentBuilder()是一个抽象方法,其实现细节在DocumentBuilderFactory类的子类中。

// OpenJDK 15
package javax.xml.parsers;

public abstract class DocumentBuilderFactory {
    ...
    public static DocumentBuilderFactory newInstance() {
        return FactoryFinder.find(DocumentBuilderFactory.class, DEFAULT_IMPL);
    }

    public abstract DocumentBuilder newDocumentBuilder() throws ParserConfigurationException;
    ...
}

在DocumentBuilderFactory的子类DocumentBuilderFactoryImpl中,newDocumentBuild()方法返回一个DocumentBuilder对象,并在该对象中设置了多项相关的配置信息,以保证DocumentBuilder对象能正常工作。

// OpenJDK 15
package com.sun.org.apache.xerces.internal.jaxp;

public class DocumentBuilderFactoryImpl extends DocumentBuilderFactory {
    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        if (grammar != null && attributes != null) {
            ...
        }

        try {
            return new DocumentBuilderImpl(this, attributes, features, fSecureProcess);
        } catch (SAXException se) {
            throw new ParserConfigurationException(se.getMessage());
        }
    }
}

在使用DocumentBuilderFactory对象创建XML文档的过程中,我们首先会用abstractFactory对象生成出一个factory对象。这个factory对象是用于创建Document对象的。因此,DocumentBuilderFactory使用的是抽象工厂模式。

public class AbstractFactoryClient {
    public static void main(String[] args) {
        String xml = "<document><body><guitar>Ibanez</guitar><guitar>Gibson</guitar></body></document>";
		ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());

        DocumentBuilderFactory abstractFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder factory = abstractFactory.newDocumentBuilder();
        Document doc = factory.parse(bais);
        doc.getDocumentElement().normalize();

		System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
    }
}

5 小结

本章介绍了抽象工厂模式的结构和使用方法。抽象工厂模式在工厂模式的基础上新增加了一层抽象,将创建出的不同类型的对象有效的结合起来,创建出一个新的、更复杂的对象。这种设计方法能够分离对象之间的逻辑关系,帮助开发人员简化对象使用端的代码逻辑。

 

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.