JavaRush /Java 博客 /Random-ZH /设计模式:抽象工厂

设计模式:抽象工厂

已在 Random-ZH 群组中发布
你好!今天我们继续学习设计模式,聊聊抽象工厂设计模式:AbstractFactory - 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 gui factory 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 gui factory 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 OrderCoffeeForm {
    private final TextField customerNameTextField;
    private final Select coffeTypeSelect;
    private final Button orderButton;

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

    public void drawOrderCoffeeForm() {
        // Определим Name операционной системы, получив meaning системной проперти через System.getProperty
        String osName = System.getProperty("os.name").toLowerCase();
        GUIFactory guiFactory;

        if (osName.startsWith("win")) { // Для windows
            guiFactory = new WindowsGUIFactory();
        } else if (osName.startsWith("mac")) { // Для mac
            guiFactory = new MacGUIFactory();
        } else {
            System.out.println("Unknown OS, can't draw form :( ");
            return;
        }
        orderCoffeeForm = new OrderCoffeeForm(guiFactory);
    }

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

Creating gui factory for Windows OS
Creating order coffee form
Creating TextField for Windows OS
Creating Select for Windows OS
Creating Button for Windows OS
在 Mac 上,输出如下:

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

Unknown OS, can't draw form :( 
好吧,你和我得出以下结论。我们为 GUI 应用程序编写了一个框架,该框架准确地创建了适合给定操作系统的界面元素。让我们简单地重复一下我们所创建的内容:
  • 产品系列:输入字段、选择字段和按钮。
  • 该系列产品的各种实现,适用于 Windows 和 macOS。
  • 一个抽象工厂,我们在其中定义了用于创建产品的接口。
  • 我们工厂的两个实现,每个实现负责创建特定的产品系列。
  • 一个表单,一个 Java 类,其字段是抽象接口元素,这些元素在构造函数中使用抽象工厂用所需的值进行初始化。
  • 应用类。在其中,我们创建一个表单,通过该表单将工厂所需的实现传递给构造函数。
Total:我们已经实现了抽象工厂模式。

抽象工厂:使用说明

抽象工厂是一种设计模式,用于管理不同产品系列的创建,而不依赖于特定的产品类别。使用此模板时,您必须:
  1. 自行定义产品系列。假设我们有两个:
    • SpecificProductA1,SpecificProductB1
    • SpecificProductA2,SpecificProductB2
  2. 对于该系列中的每个产品,定义一个抽象类(接口)。在我们的例子中是:
    • ProductA
    • ProductB
  3. 在每个产品系列中,每个产品都必须实现步骤 2 中定义的接口。
  4. 创建一个抽象工厂,并为步骤 2 中定义的每个产品创建方法。在我们的例子中,这些方法将是:
    • ProductA createProductA();
    • ProductB createProductB();
  5. 创建抽象工厂的实现,以便每个实现控制同一系列产品的创建。为此,在抽象工厂的每个实现中,有必要实现所有创建方法,以便在其中创建并返回产品的具体实现。
下面是一个 UML 图,说明了上述指令: 设计模式:AbstractFactory - 3现在让我们编写该指令的代码:
// Определим общие интерфейсы продуктов
public interface ProductA {}
public interface ProductB {}

// Создадим различные реализации (семейства) наших продуктов
public class SpecificProductA1 implements ProductA {}
public class SpecificProductB1 implements ProductB {}

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

// Создадим абстрактную фабрику
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
public class SpecificFactory1 implements AbstractFactory {

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

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

// Создадим реализацию абстрактной фабрики для создания продуктов семейства 1
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