CodeGym /Java 博客 /随机的 /设计模式:抽象工厂
John Squirrels
第 41 级
San Francisco

设计模式:抽象工厂

已在 随机的 群组中发布
你好!今天我们继续研究设计模式,我们将讨论抽象工厂模式。 设计模式:抽象工厂 - 1以下是我们将在课程中介绍的内容:
  • 我们将讨论什么是抽象工厂以及这种模式解决了什么问题
  • 我们将创建跨平台应用程序的框架,用于通过用户界面订购咖啡
  • 我们将研究如何使用此模式的说明,包括查看图表和代码
  • 作为奖励,本课程包含一个隐藏的彩蛋,它将帮助您学习如何使用 Java 确定操作系统的名称,并根据结果执行一个或另一个操作。
要完全理解此模式,您需要精通以下主题:
  • Java中的继承
  • Java 中的抽象类和方法

抽象工厂解决什么问题?

像所有工厂模式一样,抽象工厂帮助我们确保正确创建新对象。我们用它来管理各种互连对象系列的“生产”。各种相互关联的对象系列......这是什么意思?别担心:在实践中,一切都比看起来简单。首先,相互关联的对象家族可能是什么?假设我们正在制定一项涉及多种类型单位的军事战略:
  • 步兵
  • 骑兵
  • 弓箭手
这些类型的单位是相互关联的,因为它们在同一支军队中服役。我们可以说上面列出的类别是一系列相互关联的对象。我们明白这一点。但是抽象工厂模式用于安排创建各种相互关联的对象系列。这里也没有什么复杂的。让我们继续以军事战略为例。一般来说,军事单位属于几个不同的交战方。根据站在哪一边,军事单位的外观可能会有很大差异。罗马军队的步兵、骑兵和弓箭手与维京步兵、骑兵和弓箭手不同。在军事战略中,不同军队的士兵是相互关联的对象的不同家族。如果一个程序员'会很有趣' 这个错误导致一名身穿拿破仑时代法国制服、手持火枪的士兵走在罗马步兵的队伍中。抽象工厂设计模式正是解决这个问题所需要的。不,不是时间旅行带来的尴尬问题,而是创建各种相互关联的对象组的问题。抽象工厂提供用于创建所有可用产品(对象族)的接口。一个抽象工厂通常有多个实现。他们每个人都负责创建一个家庭的产品。我们的军事战略将包括一个创建抽象步兵、弓箭手和骑兵的抽象工厂,以及该工厂的实现。例如,一家生产罗马军团士兵的工厂和一家生产迦太基士兵的工厂。抽象是该模式最重要的指导原则。工厂的客户仅通过抽象接口与工厂及其产品一起工作。因此,您不必考虑当前正在创建哪些士兵。相反,您将此职责传递给抽象工厂的一些具体实现。

让我们继续自动化我们的咖啡店

上一课,我们研究了工厂方法模式。我们用它来扩展我们的咖啡业务并开设了几个新地点。今天,我们将继续实现业务现代化。使用抽象工厂模式,我们将为在线订购咖啡的新桌面应用程序奠定基础。在编写桌面应用程序时,我们应该始终考虑跨平台支持。我们的应用程序必须同时在 macOS 和 Windows 上运行(剧透:对 Linux 的支持留给您作为家庭作业来实现)。我们的应用程序会是什么样子?非常简单:它将是一个由文本字段、选择字段和按钮组成的表单。如果您有使用不同操作系统的经验,您肯定会注意到 Windows 上的按钮呈现方式与 Mac 上的不同。和其他一切一样……好吧,让我们开始吧。
  • 纽扣
  • 文本字段
  • 选择字段
免责声明:在每个接口中,我们可以定义诸如onClickonValueChanged或 之类的方法onInputChanged。换句话说,我们可以定义允许我们处理各种事件的方法(按下按钮、输入文本、在选择框中选择一个值)。这里特意省略了所有这些,以免示例过载并在我们研究工厂模式时使其更加清晰。让我们为我们的产品定义抽象接口:

public interface Button {}
public interface Select {}
public interface TextField {}
对于每个操作系统,我们必须创建操作系统风格的界面元素。我们将为 Windows 和 MacOS 编写代码。让我们为 Windows 创建实现:

public class WindowsButton implements Button {
}

public class WindowsSelect implements Select {
}

public class WindowsTextField implements TextField {
}
现在我们对 MacOS 做同样的事情:

public class MacButton implements Button {
}

public class MacSelect implements Select {
}

public class MacTextField implements TextField {
}
出色的。现在我们可以继续我们的抽象工厂,它将创建所有可用的抽象产品类型:

public interface GUIFactory {

    Button createButton();
    TextField createTextField();
    Select createSelect();

}
高超。如您所见,我们还没有做任何复杂的事情。接下来的一切也很简单。通过类比产品,我们为每个操作系统创建各种工厂实现。让我们从 Windows 开始:

public class WindowsGUIFactory implements GUIFactory {
    public WindowsGUIFactory() {
        System.out.println("Creating GUIFactory for Windows OS");
    }

    public Button createButton() {
        System.out.println("Creating Button for Windows OS");
        return new WindowsButton();
    }

    public TextField createTextField() {
        System.out.println("Creating TextField for Windows OS");
        return new WindowsTextField();
    }

    public Select createSelect() {
        System.out.println("Creating Select for Windows OS");
        return new WindowsSelect();
    }
}
我们在方法和构造函数中添加了一些控制台输出,以进一步说明正在发生的事情。现在对于 macOS:

public class MacGUIFactory implements GUIFactory {
    public MacGUIFactory() {
        System.out.println("Creating GUIFactory for macOS");
    }

    @Override
    public Button createButton() {
        System.out.println("Creating Button for macOS");
        return new MacButton();
    }

    @Override
    public TextField createTextField() {
        System.out.println("Creating TextField for macOS");
        return new MacTextField();
    }

    @Override
    public Select createSelect() {
        System.out.println("Creating Select for macOS");
        return new MacSelect();
    }
}
请注意,每个方法签名都表明该方法返回一个抽象类型。但在方法内部,我们正在创建产品的特定实现。这是我们控制特定实例创建的唯一地方。现在是时候为表单编写一个类了。这是一个 Java 类,其字段是接口元素:

public class CoffeeOrderForm {
    private final TextField customerNameTextField;
    private final Select coffeeTypeSelect;
    private final Button orderButton;

    public CoffeeOrderForm(GUIFactory factory) {
        System.out.println("Creating coffee order form");
        customerNameTextField = factory.createTextField();
        coffeeTypeSelect = factory.createSelect();
        orderButton = factory.createButton();
    }
}
创建界面元素的抽象工厂被传递给表单的构造函数。我们会将必要的工厂实现传递给构造函数,以便为特定操作系统创建界面元素。

public class Application {
    private CoffeeOrderForm coffeeOrderForm;

    public void drawCoffeeOrderForm() {
        // Determine the name of the operating system through System.getProperty()
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // For Windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // For Mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS. Unable to draw form :(");
            return;
        }
        coffeeOrderForm = new CoffeeOrderForm(guiFactory);
    }

    public static void main(String[] args) {
        Application application = new Application();
        application.drawCoffeeOrderForm();
    }
}
如果我们在 Windows 上运行该应用程序,我们会得到以下输出:

Creating GUIFactory for Windows OS
Creating coffee order form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
在 Mac 上,输出将如下所示:

Creating GUIFactory for macOS
Creating coffee order form
Creating TextField for macOS
Creating Select for macOS
Creating Button for macOS
在 Linux 上:

Unknown OS. Unable to draw form :( 
现在我们总结一下。我们编写了一个基于 GUI 的应用程序的框架,其中的界面元素是专门为相关操作系统创建的。我们将简洁地重复我们创建的内容:
  • 由输入字段、选择字段和按钮组成的产品系列。
  • 适用于 Windows 和 macOS 的产品系列的不同实现。
  • 定义用于创建我们产品的接口的抽象工厂。
  • 我们工厂的两个实现,每个负责创建一个特定的产品系列。
  • 一种表单(Java 类),其字段是抽象接口元素,使用抽象工厂在构造函数中使用必要的值进行初始化。
  • 应用程序类 在这个类中,我们创建一个表单,将所需的工厂实现传递给它的构造函数。
结果是我们实现了抽象工厂模式。

抽象工厂:如何使用

抽象工厂是一种设计模式,用于管理各种产品系列的创建,而无需绑定到具体的产品类。使用此模式时,您必须:
  1. 定义产品系列。假设我们有两个:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. 对于系列中的每个产品,定义一个抽象类(接口)。在我们的例子中,我们有:
    • ProductA
    • ProductB
  3. 在每个产品系列中,每个产品都必须实现步骤 2 中定义的接口。
  4. 创建一个抽象工厂,其中包含用于创建步骤 2 中定义的每个产品的方法。在我们的例子中,这些方法将是:
    • ProductA createProductA();
    • ProductB createProductB();
  5. 创建抽象工厂实现,以便每个实现控制单个系列产品的创建。为此,在抽象工厂的每个实现中,您需要实现所有创建方法,以便它们创建并返回特定的产品实现。
下面的 UML 图说明了上面概述的指令: 设计模式:抽象工厂 - 3现在我们将根据这些指令编写代码:

    // Define common product interfaces
    public interface ProductA {}
    public interface ProductB {}

    // Create various implementations (families) of our products
    public class SpecificProductA1 implements ProductA {}
    public class SpecificProductB1 implements ProductB {}

    public class SpecificProductA2 implements ProductA {}
    public class SpecificProductB2 implements ProductB {}

    // Create an abstract factory
    public interface AbstractFactory {
        ProductA createProductA();
        ProductB createProductB();
    }

    // Implement the abstract factory in order to create products in family 1
    public class SpecificFactory1 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA1();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB1();
        }
    }

    // Implement the abstract factory in order to create products in family 2
    public class SpecificFactory2 implements AbstractFactory {

        @Override
        public ProductA createProductA() {
            return new SpecificProductA2();
        }

        @Override
        public ProductB createProductB() {
            return new SpecificProductB2();
        }
    }

家庭作业

要加固材料,您可以做两件事:
  1. 改进咖啡订购应用程序,使其也可以在 Linux 上运行。
  2. 创建您自己的抽象工厂,用于生产参与任何军事战略的单位。这可以是涉及真实军队的历史军事战略,也可以是涉及兽人、侏儒和精灵的幻想战略。重要的是选择你感兴趣的东西。发挥创意,在控制台上打印消息,并享受学习模式的乐趣!
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION