C++设计模式:抽象工厂模式

Posted CodeBowl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++设计模式:抽象工厂模式相关的知识,希望对你有一定的参考价值。

抽象工厂模式

抽象工厂模式,亦称Abstract Factory。
是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

第一个例子:文具

问题引入

假设我们是一家文具店生产厂家,用代码模仿的话,我们需要代码中有一些类,用于表示:

  1. 一系列产品,例如笔、本子和尺子等。
  2. 系列产品中的不同类型,例如小学生套装(铅笔、田字格本、直尺)、中学生套装(钢笔、横线本、三角尺)、大学生套餐(圆珠笔、白纸本、尺子)。

我们需要单独生成每件文具对象,并且确保套餐中风格一致。如果大学生收到了小学生套餐的铅笔可就不好了。
此外,我们也不希望未来在套餐中添加新产品去修该原有代码,放在实际生产中,厂家也不会因为添加新产品,去修改原有生产线,在代码中我们也不想频繁地修改核心代码!

解决方案

抽象工厂模式就解决了我们这种问题!
抽象工厂模式建议为每类产品明确声明接口(例如笔、本子、尺子)。然后确保不同套餐中都继承这些接口,例如所有类型的笔,都要继承笔的接口!

接下来我们需要声明抽象工厂,包含套餐中所有产品构造方法的接口,也就是创建实例的办法,例如创建笔CreatePen()、创建本子CreateNoteBook()、创建尺子CreaterRuler()。
这些方法必须返回抽象产品类型,即我们之前抽象的接口:笔、本子和尺子。
那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于 抽象工厂接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如, 小学生套餐pupil只能生产铅笔、田字格本和直尺。
客户端代码可以通过相应的抽象接口调用工厂和产品类。 你无需修改实际客户端代码, 就能更改传递给客户端的工厂类, 也能更改客户端代码接收的产品变体。

假设客户端想要工厂创建一把尺子。 客户端无需了解工厂类, 也不用管工厂类创建出的尺子类型。 无论是小学生、初中生, 对于客户端来说没有分别, 它只需调用抽象尺子接口就可以了。 这样一来, 客户端只需知道椅子以某种方式实现尺子的作用就足够了。 此外, 无论工厂返回的是何种尺子变体, 它都会和由同一工厂对象创建的本子和笔是一个套餐。

第二个问题家具

第二个问题作为一个思考题。

问题引入

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

  1. 一系列相关产品, 例如 椅子Chair 、 ​ 沙发Sofa和 咖啡桌Coffee­Table 。
  2. 系列产品的不同变体。 例如, 你可以使用 现代Modern 、 ​ 维多利亚Victorian 、 ​ 装饰风艺术Art­Deco等风格生成 椅子 、 ​ 沙发和 咖啡桌 。

你需要设法单独生成每件家具对象, 这样才能确保其风格一致。 如果顾客收到的家具风格不一样, 他们可不会开心。

此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁, 你不会想在每次更新时都去修改核心代码的。

解决方案

答案在这里https://refactoringguru.cn/design-patterns/abstract-factory

抽象工厂模式结构

  1. 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
  2. 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品(椅子/沙发)。
  3. 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
  4. 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
  5. 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象,
    就能与任何具体工厂/产品变体交互。

抽象工厂模式适合应用场景

  • 如果代码需要与多个不同系列的相关产品交互, 但是由于无法提前获取相关信息, 或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建, 在这种情况下, 你可以使用抽象工厂。

  • 抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。 只要代码通过该接口创建对象,那么你就不会生成与应用程序已生成的产品类型不一致的产品。

  • 如果你有一个基于一组抽象方法的类, 且其主要功能因此变得不明确, 那么在这种情况下可以考虑使用抽象工厂模式。

  • 在设计良好的程序中, 每个类仅负责一件事。 如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。

实现方式

  1. 以不同的产品类型与产品变体为维度绘制矩阵。
  2. 为所有产品声明抽象产品接口。 然后让所有具体产品类实现这些接口。
  3. 声明抽象工厂接口, 并且在接口中为所有抽象产品提供一组构建方法。
  4. 为每种产品变体实现一个具体工厂类。
  5. 在应用程序中开发初始化代码。 该代码根据应用程序配置或当前环境, 对特定具体工厂类进行初始化。 然后将该工厂对象传递给所有需要创建产品的类。
  6. 找出代码中所有对产品构造函数的直接调用, 将其替换为对工厂对象中相应构建方法的调用。

抽象工厂模式优缺点

  • 你可以确保同一工厂生成的产品相互匹配。
  • 你可以避免客户端和具体产品代码的耦合。
  • 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
  • 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法模式 (较为简单, 而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂模式、
    原型模式或生成器模式 (更灵活但更加复杂)。
  • 生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品,
    生成器则允许你在获取产品前执行一些额外构造步骤。
  • 抽象工厂模式通常基于一组工厂方法, 但你也可以使用原型模式来生成这些类的方法。
  • 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂来代替外观模式。
  • 你可以将抽象工厂和桥接模式搭配使用。 如果由桥接定义的抽象只能与特定实现合作, 这一模式搭配就非常有用。 在这种情况下,
    抽象工厂可以对这些关系进行封装, 并且对客户端代码隐藏其复杂性。
  • 抽象工厂、 生成器和原型都可以用单例模式来实现。

参考资料

https://refactoringguru.cn/design-patterns/abstract-factory
漫画:什么是 “抽象工厂模式” ?

以上是关于C++设计模式:抽象工厂模式的主要内容,如果未能解决你的问题,请参考以下文章

C++设计模式:抽象工厂模式

C++设计模式:抽象工厂模式

设计模式 C++抽象工厂模式

C++实现工厂模式(简单工厂模式工厂方法模式抽象工厂模式)

抽象工厂模式(C++)

设计模式之抽象工厂模式(C++)