设计模式——抽象工厂

抽象工厂

目的

抽象工厂是一种创建型的设计模式,它允许您生成一系列相关的对象而无需指定它们的具体类。
设计模式——抽象工厂_第1张图片

问题

假设您正在创建一个家具商店模拟器。你的代码由以下类组成:

  1. 系列相关产品,如:Chair + Sofa + CoffeeTable

  2. 这个家族的几个派生。例如Chair + Sofa + CoffeeTable产品有ModernVictorianArtDeco等多种版本。

设计模式——抽象工厂_第2张图片
您需要一种方法来创建单独的家具对象以便它们与同类的其他对象相匹配。顾客收到不匹配的家具时会很生气。

设计模式——抽象工厂_第3张图片
此外在向程序添加新产品或产品族时,您不希望更改现有代码。家具供应商经常更新他们的目录,您不会希望每次都更改核心代码。

解决方案

抽象工厂模式建议的第一件事是为产品家族的每个不同产品(例如,椅子、沙发或茶几)显式地声明接口。然后您可以让所有具体产品遵循这些接口。例如所有椅子变体都可以实现chair接口;所有的咖啡桌变体都可以实现CoffeeTable接口。

设计模式——抽象工厂_第4张图片
下一步是声明抽象工厂——一个包含所有产品的创建方法列表的接口(例如createChaircreateSofacreateCoffeeTable)。这些方法必须返回由我们前面提取的接口表示的抽象产品类型:ChairSofaCoffeeTable等等。
设计模式——抽象工厂_第5张图片
现在具体产品呢?对于产品系列的具体产品,我们基于AbstractFactory接口创建一个单独的工厂类。工厂是返回特定类型产品的类。例如ModernFurnitureFactory只能创建ModernChairModernSofaModernCoffeeTable物件。

客户端代码必须通过各自的抽象接口与工厂和产品一起工作。这允许您更改传递给客户端代码的工厂类型以及客户端代码接收的产品变体而不会破坏实际的客户端代码。

设计模式——抽象工厂_第6张图片

结构

设计模式——抽象工厂_第7张图片

  1. 抽象产品为组成产品族的一组相关的产品统一声明接口。

  2. 具体产品是抽象产品的各种实现,按类型分组。具体产品(Victorian/Modern)必须实现抽象产品的接口。

  3. 抽象工厂接口声明了一组创建抽象产品的方法。

  4. 具体工厂实现了抽象工厂的创建方法。每个具体工厂对应于一组具体产品并且只创建这些具体产品。

  5. 虽然具体的工厂实例化具体的产品,但是它们的创建方法的签名必须返回相应的抽象产品。这样,使用工厂的客户机代码就不会与它从工厂获得的产品的特定变体耦合。客户端可以使用任何具体的工厂/产品变体,只要它通过抽象接口与它们的对象通信。

伪码

这个例子说明了如何使用抽象工厂模式来创建跨平台的UI元素而不用将客户端代码耦合到具体的UI类,同时保持所有创建的元素与所选的操作系统一致。

设计模式——抽象工厂_第8张图片
在跨平台应用程序中相同的UI元素的行为应该是相似的,但在不同的操作系统下看起来会有些不同。此外确保UI元素与当前操作系统的风格匹配是您的工作。您不希望您的程序在Windows中执行时呈现macOS控件。

抽象工厂接口声明了一组创建方法,客户端代码可以使用这些方法来生成不同类型的UI元素。具体的工厂对应于特定的操作系统并创建与特定操作系统相匹配的UI元素。

它是这样工作的:当一个应用程序启动时,它检查当前操作系统的类型。应用程序使用这个信息从一个匹配操作系统的类中创建一个工厂对象。剩下的代码使用这个工厂来创建UI元素。这可以防止创建错误的元素。

使用这种方法,客户端代码不依赖于工厂和UI元素的具体类只要它通过抽象接口处理这些对象即可。这也允许客户端代码支持将来可能添加的其他工厂或UI元素。

因此你不需要每次在你的应用程序中添加一个新的UI元素变体时修改客户端代码。你只需要创建一个新的工厂类来产生这些元素并稍微修改应用程序的初始化代码,以便它在适当的时候选择那个类。

// The abstract factory interface declares a set of methods that
// return different abstract products. These products are called
// a family and are related by a high-level theme or concept.
// Products of one family are usually able to collaborate among
// themselves. A family of products may have several variants,
// but the products of one variant are incompatible with the
// products of another variant.
interface GUIFactory is
    method createButton():Button
    method createCheckbox():Checkbox


// Concrete factories produce a family of products that belong
// to a single variant. The factory guarantees that the
// resulting products are compatible. Signatures of the concrete
// factory's methods return an abstract product, while inside
// the method a concrete product is instantiated.
class WinFactory implements GUIFactory is
    method createButton():Button is
        return new WinButton()
    method createCheckbox():Checkbox is
        return new WinCheckbox()

// Each concrete factory has a corresponding product variant.
class MacFactory implements GUIFactory is
    method createButton():Button is
        return new MacButton()
    method createCheckbox():Checkbox is
        return new MacCheckbox()


// Each distinct product of a product family should have a base
// interface. All variants of the product must implement this
// interface.
interface Button is
    method paint()

// Concrete products are created by corresponding concrete
// factories.
class WinButton implements Button is
    method paint() is
        // Render a button in Windows style.

class MacButton implements Button is
    method paint() is
        // Render a button in macOS style.

// Here's the base interface of another product. All products
// can interact with each other, but proper interaction is
// possible only between products of the same concrete variant.
interface Checkbox is
    method paint()

class WinCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in Windows style.

class MacCheckbox implements Checkbox is
    method paint() is
        // Render a checkbox in macOS style.


// The client code works with factories and products only
// through abstract types: GUIFactory, Button and Checkbox. This
// lets you pass any factory or product subclass to the client
// code without breaking it.
class Application is
    private field factory: GUIFactory
    private field button: Button
    constructor Application(factory: GUIFactory) is
        this.factory = factory
    method createUI() is
        this.button = factory.createButton()
    method paint() is
        button.paint()


// The application picks the factory type depending on the
// current configuration or environment settings and creates it
// at runtime (usually at the initialization stage).
class ApplicationConfigurator is
    method main() is
        config = readApplicationConfigFile()

        if (config.OS == "Windows") then
            factory = new WinFactory()
        else if (config.OS == "Mac") then
            factory = new MacFactory()
        else
            throw new Exception("Error! Unknown operating system.")

        Application app = new Application(factory)

应用场景

当您的代码需要使用各种系列的相关产品,但您不希望它依赖于这些产品的具体类——它们可能事先不为人知或您希望允许未来的可扩展性时,使用抽象工厂。

抽象工厂为您提供了一个从产品家族的每个类中创建对象的接口。只要你的代码通过这个接口创建对象,你就不必担心创建的产品的错误变体与你的应用程序已经创建的产品不匹配。

  • 当你有一个类,它有一组模糊了它的主要职责的工厂方法时,考虑实现抽象工厂。

  • 在一个设计良好的程序中,每个类只负责一件事。当一个类处理多个产品类型时,可能有必要将其工厂方法提取到独立的工厂类或成熟的抽象工厂实现中。

如何实现

  1. 绘制出不同产品类型和这些产品变体的矩阵。
  2. 为所有产品类型声明抽象产品接口。然后让所有具体的产品类实现这些接口。
  3. 使用所有抽象产品的一组创建方法声明抽象工厂接口。
  4. 实现一组具体的工厂类,每个产品变体一个。
  5. 在应用程序的某个地方创建工厂初始化代码。它应该实例化一个具体的工厂类,这取决于应用程序配置或当前环境。将这个工厂对象传递给所有构造产品的类。
  6. 扫描代码并找到所有对产品构造函数的直接调用。将它们替换为对工厂对象的适当创建方法的调用。

正反面

正面因素:

  • 你可以确定你从工厂得到的产品是相互兼容的。
  • 您可以避免具体产品和客户端代码之间的紧密耦合。
  • 单一责任原则。您可以将产品创建代码提取到一个地方使代码更易于支持。
  • 打开/关闭原则。您可以在不破坏现有客户端代码的情况下引入新的产品变体。

反面因素:

  • 代码可能会变得比它应该的更加复杂,因为随着模式引入了许多新的接口和类。

同其他设计模式的关系

  • 许多设计开始使用工厂方法(更简单,更可通过子类定制),然后发展到抽象工厂原型构建器(更灵活,但更复杂)。

  • 构建器专注于一步一步地构建复杂对象。抽象工厂专门用于创建相关对象族。抽象工厂立即返回产品,而构建器允许您在获取产品之前运行一些额外的构造步骤。

  • 抽象工厂类通常基于一组工厂方法,但您也可以使用原型模式来组合这些类上的方法。

  • 当您只想隐藏从客户端代码创建子系统对象的方式时,抽象工厂可以作为外观模式的替代方案。

  • 你可以同时使用抽象工厂桥模式。当桥模式定义的某些抽象只能用于特定的实现时,这种配对很有用。在这种情况下,抽象工厂可以封装这些关系并对客户端代码隐藏复杂性。

  • 抽象工厂构建器原型都可以实现为单例

你可能感兴趣的:(C++,设计模式,抽象工厂,C++)