抽象工厂模式

51 阅读5分钟

模式介绍

抽象工厂模式提供了一种方式,允许接口创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式主要解决的问题是当需要生成一系列相关或相互依赖的对象时,如何避免设计时就确定具体的类。

产品族

指的是一组具有共同主题或共同属性的产品。这些产品通常可以一起使用,或者它们之间存在某种逻辑关系。

产品等级结构

指的是产品之间的层次关系,比如组件、部件和产品之间的关系。在产品等级结构中,产品可以是另一个产品的组成部分。

模式图解

UML类图

image.png

在这里,产品族为 Win、Mac,产品等级结构为:Button、textBox。

类图说明

抽象工厂模式涉及到以下的角色:

  • 抽象产品(Abstract Product)角色:担任这个角色的类是工厂方法模式所创建的对象的父类,或它们共同拥有的接口。通常使用 Java 接口或者抽象 Java 类实现这一角色。

  • 具体产品(Concrete Product)角色:抽象工厂模式所创建的任何产品对象都是某一个具体产品类的实例。这个是客户端最终需要的东西,其内部一定充满了应用系统的商业逻辑。通常使用具体 Java 类实现这个角色。

  • 抽象工厂(AbstractFactory)角色:担任这个角色的是工厂方法模式的核心,它是与应用系统的商业逻辑无关的。通常使用 Java 接口或者抽象 Java 类实现,而所有的具体工厂类必须实现这个 Java 接口或继承这个抽象 Java 类。

  • 具体工厂(Concrete Factory)角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧急相关的。通常使用具体 Java 类实现这个角色。

示例代码

定义抽象产品

/**
 * 抽象产品:按钮
 *
 * @author Asyyr
 * @create 20241201
 */
public interface Button {
    void paint();
}
/**
 * 抽象产品:文本框
 *
 * @author Asyyr
 * @create 20241201
 */
public interface TextBox {
    void paint();
}

定义具体产品

/**
 * 具体产品:Windows按钮
 *
 * @author Asyyr
 * @create 20241201
 */
class WinButton implements Button {
    public void paint() {
        System.out.println("Painting Windows Button");
    }
}
/**
 * 具体产品:MacOS按钮
 *
 * @author Asyyr
 * @create 20241201
 */
class MacButton implements Button {
    public void paint() {
        System.out.println("Painting MacOS Button");
    }
}
/**
 * 具体产品:Windows文本框
 *
 * @author Asyyr
 * @create 20241201
 */
class WinTextBox implements TextBox {
    public void paint() {
        System.out.println("Painting Windows TextBox");
    }
}
/**
 * 具体产品:MacOS文本框
 *
 * @author Asyyr
 * @create 20241201
 */
class MacTextBox implements TextBox {
    public void paint() {
        System.out.println("Painting MacOS TextBox");
    }
}

定义抽象工厂

/**
 * 抽象工厂
 *
 * @author Asyyr
 * @create 20241201
 */
public interface GUIFactory {

    Button createButton();

    TextBox createTextBox();
}

实现具体工厂

/**
 * 具体工厂:Windows工厂
 *
 * @author Asyyr
 * @create 20241201
 */
public class WinFactory implements GUIFactory {
    public Button createButton() {
        return new WinButton();
    }

    public TextBox createTextBox() {
        return new WinTextBox();
    }
}
/**
 * 具体工厂:MacOS工厂
 *
 * @author Asyyr
 * @create 20241201
 */
public class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }

    public TextBox createTextBox() {
        return new MacTextBox();
    }
}

验证抽象工厂

/**
 * 抽象工厂客户端
 *
 * @author Asyyr
 * @create 20241201
 */
public class Client {
    public static void main(String[] args) {
        GUIFactory factory;

        // 根据需要选择不同的工厂
        factory = new WinFactory();
        Button button = factory.createButton();
        TextBox textBox = factory.createTextBox();

        button.paint();
        textBox.paint();
    }
}

如何支持开闭原则

开闭原则要求一个软件系统可以在不修改原有代码的情况下,通过扩展达到增强其功能的目的。对于一个涉及到多个产品等级结构和多个产品族的系统,其功能的增强不外乎两个方面:

  • 增加新的产品族。

在产品等级结构的数目不变的情况下,增加新的产品族,就意味着在每一个产品等级结构中增加一个(或者多个)新的具体(或者抽象和具体)产品角色。

由于工厂等级结构是与产品等级结构平行的,因此,当产品等级结构有所调整时,需要将工厂等级结构做相应的调整。现在产品等级结构出现了新的元素,因此,需要向工程等级结构中加入相应的新元素就可以了。

换言之,只需要向系统中加入新的具体工程类就可以了,没有必要修改已有的工厂角色或者产品角色。因此,在系统中的产品族增加时,抽象工厂模式是支持开闭原则的。

  • 增加新的产品等级结构。

在产品族的数目不变的情况下,增加新的产品等级结构。换言之,所有的产品等级结构中的产品数目不会改变,但是现在多出一个与现有的产品等级结构平行的新的产品等级结构。

要做到这一点,就需要修改所有的工厂角色,给每一个工厂类都增加一个新的工厂方法,而这显然是违背开闭原则的。

换言之,对于产品等级结构的增加,抽象工厂模式是不支持开闭原则的。

综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。

抽象工厂模式使用场景

  • 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
  • 这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品;
  • 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
  • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。