一文读懂抽象工厂方法模式

01 意图

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

image.png

02 问题

想象一下,您正在创建一个家具店模拟器。您的代码由代表的类组成:

  1. 一系列相关产品,例如:Chair++ 。Sofa``CoffeeTable

  2. 这个家族的几个变种。例如,产品Chair++有以下变体Sofa:、、。CoffeeTable``Modern``Victorian``ArtDeco

image

您需要一种方法来创建单个家具对象,以便它们与同一系列的其他对象相匹配。当客户收到不匹配的家具时,他们会非常生气。

[图片上传中...(image-b3c8e2-1642771338019-1)]

此外,在向程序添加新产品或产品系列时,您不希望更改现有代码。家具供应商经常更新他们的目录,您不会希望每次都更改核心代码。

03 解决方案

抽象工厂模式建议的第一件事是为产品系列的每个不同产品(例如,椅子、沙发或咖啡桌)显式声明接口。然后,您可以使所有产品变体都遵循这些接口。例如,所有椅子变体都可以实现该Chair接口;所有咖啡桌变体都可以实现该CoffeeTable接口,依此类推。

image

下一步是声明抽象工厂——一个接口,其中包含产品系列中所有产品的创建方法列表(例如createChair,createSofa和createCoffeeTable)。这些方法必须返回由我们之前提取的接口表示的抽象产品类型:Chair、Sofa等CoffeeTable

image

现在,产品变体怎么样?对于产品系列的每个变体,我们基于AbstractFactory接口创建一个单独的工厂类。工厂是返回特定种类产品的类。例如,ModernFurnitureFactory只能创建ModernChair,ModernSofaModernCoffeeTable对象。

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

[图片上传中...(image-c207b-1642771338018-0)]

假设客户想要一家工厂生产一把椅子。客户不必知道工厂的等级,也不必关心它得到什么样的椅子。无论是现代模型还是维多利亚风格的椅子,客户都必须使用抽象Chair接口以相同的方式对待所有椅子。使用这种方法,客户对椅子的唯一了解就是它sitOn以某种方式实现了该方法。此外,无论返回哪种椅子变体,它都将始终与同一工厂对象生产的沙发或咖啡桌类型相匹配。

还有一件事需要澄清:如果客户端只暴露给抽象接口,那么是什么创建了实际的工厂对象?通常,应用程序在初始化阶段创建一个具体的工厂对象。在此之前,应用程序必须根据配置或环境设置选择工厂类型。

04 结构实现

image

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

image

跨平台应用程序中的相同 UI 元素应具有相似的行为,但在不同的操作系统下看起来会有所不同。此外,确保 UI 元素与当前操作系统的风格相匹配是您的工作。您不希望您的程序在 Windows 中执行时呈现 macOS 控件。

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

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

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

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

// The abstract factory interface declares a set of methods that

05 适用场景

当您的代码需要与各种相关产品系列一起使用时,请使用抽象工厂,但您不希望它依赖于这些产品的具体类——它们可能事先是未知的,或者您只是想考虑到未来的可扩展性。

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

  • 当您的类具有一组模糊其主要职责的工厂方法时,请考虑实现抽象工厂。

  • 在精心设计的程序中,每个班级只负责一件事。当一个类处理多种产品类型时,可能值得将其工厂方法提取到独立的工厂类或成熟的抽象工厂实现中。

06 如何实施

  1. 绘制出不同产品类型与这些产品变体的矩阵。

  2. 为所有产品类型声明抽象产品接口。然后让所有具体的产品类实现这些接口。

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

  4. 实现一组具体的工厂类,每个产品变体一个。

  5. 在应用程序的某处创建工厂初始化代码。它应该实例化具体工厂类之一,具体取决于应用程序配置或当前环境。将此工厂对象传递给构造产品的所有类。

  6. 扫描代码并找到对产品构造函数的所有直接调用。将它们替换为对工厂对象的适当创建方法的调用。

07 优缺点

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容