笼统工厂形式( Abstract Factory)

笼统工厂形式是一种创立型规划形式, 它能创立一系列相关的目标, 而无需指定其详细类。

遇到的问题

假定你正在开发一款家具商店模拟器。 你的代码中包含一些类, 用于表示:

  1. 一系列相关产品, 例如 椅子Chair 、 沙发Sofa和 咖啡桌CoffeeTable 。
  2. 系列产品的不同变体。 例如, 你能够运用 现代Modern 、 维多利亚Victorian 、 装饰风艺术ArtDeco等风格生成 椅子沙发咖啡桌

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

系列产品及其不同变体。

你需求设法单独生成每件家具目标, 这样才干保证其风格共同。 假如顾客收到的家具风格不一样, 他们可不会高兴。

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

现代风格的沙发和维多利亚风格的椅子不搭。

此外, 你也不期望在增加新产品或新风格时修正已有代码。 家具供应商关于产品目录的更新十分频频, 你不会想在每次更新时都去修正中心代码的。

解决方案

首要, 笼统工厂形式主张为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 保证一切产品变体都承继这些接口。 例如, 一切风格的椅子都完成 椅子接口; 一切风格的咖啡桌都完成 咖啡桌接口, 以此类推。

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

同一目标的一切变体都有必要放置在同一个类层次结构之中。

接下来, 我们需求声明笼统工厂——包含系列中一切产品构造办法的接口。 例如 createChair创立椅子 、 createSofa创立沙发和 createCoffeeTable创立咖啡桌 。 这些办法有必要回来笼统产品类型, 即我们之前抽取的那些接口: 椅子沙发咖啡桌等等。

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

每个详细工厂类都对应一个特定的产品变体。

那么该怎么处理产品变体呢? 关于系列产品的每个变体, 我们都将依据 笼统工厂接口创立不同的工厂类。 每个工厂类都只能回来特定类别的产品, 例如, 现代家具工厂ModernFurnitureFactory只能创立 现代椅子ModernChair 、 现代沙发ModernSofa和 现代咖啡桌ModernCoffeeTable目标。

客户端代码能够经过相应的笼统接口调用工厂和产品类。 你无需修正实践客户端代码, 就能更改传递给客户端的工厂类, 也能更改客户端代码接纳的产品变体。

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

客户端无需了解其所调用工厂的详细类信息。

假定客户端想要工厂创立一把椅子。 客户端无需了解工厂类, 也不必管工厂类创立出的椅子类型。 无论是现代风格, 仍是维多利亚风格的椅子, 关于客户端来说没有分别, 它只需调用笼统 椅子接口就能够了。 这样一来, 客户端只需知道椅子以某种方式完成了 sitOn坐下办法就足够了。 此外, 无论工厂回来的是何种椅子变体, 它都会和由同一工厂目标创立的沙发或咖啡桌风格共同。

最后一点阐明: 假如客户端仅触摸笼统接口, 那么谁来创立实践的工厂目标呢? 一般情况下, 运用程序会在初始化阶段创立详细工厂目标。 而在此之前, 运用程序有必要依据配置文件或环境设定挑选工厂类别。

笼统工厂形式结构

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

  1. 笼统产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
  2. 详细产品 (Concrete Product) 是笼统产品的多种不同类型完成。 一切变体 (维多利亚/现代) 都有必要完成相应的笼统产品 (椅子/沙发)。
  3. 笼统工厂 (Abstract Factory) 接口声明晰一组创立各种笼统产品的办法。
  4. 详细工厂 (Concrete Factory) 完成笼统工厂的构建办法。 每个详细工厂都对应特定产品变体, 且仅创立此种产品变体。
  5. 虽然详细工厂会对详细产品进行初始化, 其构建办法签名有必要回来相应的笼统产品。 这样, 运用工厂类的客户端代码就不会与工厂创立的特定产品变体耦合。 客户端 (Client) 只需经过笼统接口调用工厂和产品目标, 就能与任何详细工厂/产品变体交互

伪代码

下面比如经过运用笼统工厂形式, 使得客户端代码无需与详细 UI 类耦合, 就能创立跨渠道的 UI 元素, 一起保证所创立的元素与指定的操作体系匹配。

2023 跟我一起学设计模式:抽象工厂模式( Abstract Factory)

跨渠道 UI 类示例。

跨渠道运用中的相同 UI 元素功用相似, 但是在不同操作体系下的外观有必定差异。 此外, 你需求保证 UI 元素与当前操作体系风格共同。 你必定不期望在 Windows 体系下运行的运用程序中显示 macOS 的控件。

笼统工厂接口声明一系列构建办法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个详细工厂对应特定操作体系, 并负责生成符合该操作体系风格的 UI 元素。

其运作方式如下: 运用程序发动后检测当前操作体系。 依据该信息, 运用程序经过与该操作体系对应的类创立工厂目标。 其他代码运用该工厂目标创立 UI 元素。 这样能够防止生成过错类型的元素。

运用这种办法, 客户端代码只需调用笼统接口, 而无需了解详细工厂类和 UI 元素。 此外, 客户端代码还支撑未来增加新的工厂或 UI 元素。

这样一来, 每次在运用程序中增加新的 UI 元素变体时, 你都无需修正客户端代码。 你只需创立一个能够生成这些 UI 元素的工厂类, 然后稍微修正运用程序的初始代码, 使其能够挑选合适的工厂类即可。

笼统工厂形式合适运用场景

假如代码需求与多个不同系列的相关产品交互, 但是因为无法提早获取相关信息, 或者出于对未来扩展性的考虑, 你不期望代码依据产品的详细类进行构建, 在这种情况下, 你能够运用笼统工厂。

笼统工厂为你供给了一个接口, 可用于创立每个系列产品的目标。 只需代码经过该接口创立目标, 那么你就不会生成与运用程序已生成的产品类型不共同的产品。

假如你有一个依据一组笼统办法的类, 且其主要功用因此变得不明确, 那么在这种情况下能够考虑运用笼统工厂形式。

在规划杰出的程序中, 每个类仅负责一件事。 假如一个类与多种类型产品交互, 就能够考虑将工厂办法抽取到独立的工厂类或具备完整功用的笼统工厂类中。

完成方式

  1. 以不同的产品类型与产品变体为维度制作矩阵
  2. 为一切产品声明笼统产品接口。 然后让一切详细产品类完成这些接口。
  3. 声明笼统工厂接口, 并且在接口中为一切笼统产品供给一组构建办法。
  4. 为每种产品变体完成一个详细工厂类。
  5. 在运用程序中开发初始化代码。 该代码依据运用程序配置或当前环境, 对特定详细工厂类进行初始化。 然后将该工厂目标传递给一切需求创立产品的类。
  6. 找出代码中一切对产品构造函数的直接调用, 将其替换为对工厂目标中相应构建办法的调用。

笼统工厂形式优缺点

  • 你能够保证同一工厂生成的产品彼此匹配。

  • 你能够防止客户端和详细产品代码的耦合。

  • 单一责任准则。 你能够将产品生成代码抽取到同一方位, 使得代码易于维护。

  • 开闭准则。 向运用程序中引进新产品变体时, 你无需修正客户端代码。

  • 因为选用该形式需求向运用中引进很多接口和类, 代码可能会比之前愈加复杂。

代码与示例

  • Golang《笼统工厂》形式讲解和代码示例 – 掘金 ()