创建型模式之抽象工厂模式

Posted by Night Field's Blog on March 20, 2020

1 概述

上一篇文章讲到了工厂方法模式,它提供了一种在不指定具体实现的情况下,创建类实例的解决方案。那为什么还需要抽象工厂模式(Abstract Factory Pattern)呢?

2 抽象工厂模式

抽象工厂模式本质上,也是定义一个工厂,用来作为类创建的入口,拥有工厂方法模式的优点:如隐藏类的构造细节,降低类的使用复杂度,与调用者解耦等等。 而它与工厂方法模式最大的区别在于,抽象工厂模式更强调创建一族的元素。比如对于不同的浏览器,有不同的按钮,选择框和输入框。那么我们可以定义一个浏览器的接口,并创建浏览器工厂ChromeFactoryFirefoxFactory。其中ChromeFactory可以创建Chrome一族的按钮,选择框,FirefoxFactory可以创建Firefox一族的按钮选择框,从而实现同一族元素的高内聚,提高程序的灵活性和可扩展性。

3 案例

再看一个简单的例子。有一个汽车工厂,可以生产轿车和SUV。

1
2
3
4
5
6
7
8
9
10
11
12
interface CarFactory {
    Car getCar();
    SUV getSUV();
}

interface Car {
    void getSize();
}

interface SUV {
    void getSize();
}

按类别,工厂又可以分为美国工厂和日本工厂。而日本工厂只能生产日本车,美国工厂只能生产美国车,这是绑定的关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class AmericanCarFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new TeslaModalS();
    }
    @Override
    public SUV getSUV() {
        return new TeslaModalX();
    }
}
public class TeslaModalS implements Car {
    @Override
    public void getSize() {
        System.out.println("Size of American Car Modal S is '4979*1964*1445'");
    }
}
public class TeslaModalX implements SUV {
    @Override
    public void getSize() {
        System.out.println("Size of American SUV Modal X is '5037*2070*1684'");
    }
}

class JapaneseCarFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new HondaAccord();
    }
    @Override
    public SUV getSUV() {
        return new HondaCRV();
    }
}
public class HondaAccord implements Car {
    @Override
    public void getSize() {
        System.out.println("Size of Japanese Car Accord is '4893*1862*1449'");
    }
}
public class HondaCRV implements SUV {
    @Override
    public void getSize() {
        System.out.println("Size of Japanese SUV C-RV is '4585*1855*1689'");
    }
}

定义了上述工厂之后,我们便将同一族的产品,限制在了对应的工厂之中。然后我们再定义一个统一的入口,便可以很容易地创建汽车了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test {
    public static void main(String[] args) throws OperationNotSupportedException {
        CarFactory japaneseCarFactory = CarFactoryProducer.createFactory(CarFactoryProducer.FactoryType.JAPANESE);
        Car japaneseCar = japaneseCarFactory.getCar();
        japaneseCar.getSize();
        SUV japaneseSUV = japaneseCarFactory.getSUV();
        japaneseSUV.getSize();

        CarFactory americanCarFactory = CarFactoryProducer.createFactory(CarFactoryProducer.FactoryType.AMERICAN);
        Car americanCar = americanCarFactory.getCar();
        americanCar.getSize();
        SUV americanSUV = americanCarFactory.getSUV();
        americanSUV.getSize();
    }
}

public abstract class CarFactoryProducer {
    enum FactoryType {
        JAPANESE, AMERICAN
    }

    public static CarFactory createFactory(FactoryType type) throws OperationNotSupportedException {
        switch (type) {
            case JAPANESE: return new JapaneseCarFactory();
            case AMERICAN: return new AmericanCarFactory();
        }
        throw new OperationNotSupportedException("type '" + type + "' is not supported");
    }
}

输出:

1
2
3
4
Size of Japanese Car Accord is '4893*1862*1449'
Size of Japanese SUV C-RV is '4585*1855*1689'
Size of American Car Modal S is '4979*1964*1445'
Size of American SUV Modal X is '5037*2070*1684'

UML: UML for abstract factory

可以看到,抽象工厂模式极大地降低了类创建与使用的复杂度,提高了同一族元素的内聚性。同时,如果需要新增一族元素比如德国车,只需新增一个GermanyCarFactory用来生产德国车即可,扩展性很好。

JDK中,DocumentBuilderFactory就运用了抽象工厂模式

1
2
3
4
public static DocumentBuilderFactory newInstance(String factoryClassName, ClassLoader classLoader){
        // 根据传入的参数,返回对应的Factory实现类
        return FactoryFinder.newInstance(DocumentBuilderFactory.class, factoryClassName, classLoader, false);
}

4 总结

抽象工厂模式提供了创建一类元素的最佳方式,一般工厂类也都以Factory字样结尾,并且创建方法返回一个子Factory。当需要创建一组属于统一类别的类,并想要对外提供一个简单的接口时,请考虑使用抽象工厂模式

文中例子的github地址