23

我已经多次看到这个问题出现在这里和那里,但我从未找到并回答我满意的答案。

来自维基百科:

Builder专注于一步一步地构建一个复杂的对象。抽象工厂强调一系列产品对象(简单或复杂)。Builder 作为最后一步返回产品,但就抽象工厂而言,产品会立即返回。

但是对于客户来说不是一样的吗?一旦构建完成,他就会获得完整的对象,因此对他来说没有额外的功能。

我认为它的唯一方法是作为一种方式或逐步组织构造函数代码,以强制构建构建器实现的结构。这很好,但与抽象工厂相差甚远。


来自 Wikipedia 的下一点是一个很好的参考,可以说明我的观点:

通常,设计从使用工厂方法(不太复杂、更可定制、子类激增)开始,然后随着设计人员发现需要更多灵活性的地方而向抽象工厂、原型或构建器(更灵活、更复杂)发展。

如果是这样,那么在您的系统中必须引入什么样的复杂性,您将从抽象工厂更改为构建器?

我的观点是,我找不到一个很明显抽象工厂不够用的例子,而你需要一个 Builder。

4

10 回答 10

13

AbstractFactory 适用于一系列相关产品。Builder 适用于一种产品。

Builder 用于逐步构建复杂的产品,而 AbstractFactory 则以抽象的方式(即,一次用于多个实现)构建不太复杂的产品。


差异对调用代码意味着什么的示例:

  • 对于使用 AbstractFactory 的客户端代码,每个方法调用都会构建一个对象。客户必须将它们组装在一起。另一方面,构建者将在内部组装它们,并将完整的对象图作为构建的最后一步。

  • 构建器允许为客户端代码一一传递和检查参数。但他可以一步构建最终产品(例如,调用构造函数,一次传入所有参数,甚至是自己构建的复杂参数)。因此,构建器的产品可能是不可变对象(或图形)(出于性能和内存原因,它在多线程环境中是无价的)。


更新以回应此评论:

Lino > 不过,它仍然困扰着我:)。我的意思是,如果您正在构建复合对象,并且您将主管拿出来,将调用构建器方法的责任传递给客户端,那么它对我有用。只有这样,建造者才会看起来非常合适。但是对于导演来说,这似乎并不多......

确切地说,Builder 没有导演,它让每个客户根据自己的具体需求创建一个复杂的产品。客户是导演,因为导演的实现没有被重用。

如果你需要多次重用一个复杂的构建过程,那么你可以有一个方法来封装它(这个方法就是你的“导演”)。

相反,对于 AbstractFactory,您通常需要一个可重用的 Director,它可以创建类似的产品(提供工厂实现)。

于 2009-12-22T15:07:34.427 回答
7

这里已经有一些非常好的答案。我只是提供一个类比。

假设你想为你的新办公室准备一张新办公桌。你去一个“工厂”看看选择,然后选择一个架子。如果它符合您的需求,那就太好了!

现在你有一个更大的办公室,墙壁形状非常奇特。现在您需要一张顶部形状适合您办公室的办公桌。而且您想要一条不同的腿来适应其余的家具,也适合您的新 Aeron 椅子。

你回到同一个工厂,没有运气。你去找可以定制“建造”你想要的东西的木匠。你给出你的规格和要求,“建造者”会告诉你某些限制。经过几次迭代,你得到了完美的办公桌!

好吧,一个蹩脚的故事,但只是为了让事情变得轻松:P

于 2009-12-22T15:21:12.727 回答
4

您需要构建器的一个很好的例子是,如果您正在基于命令行参数逐个构建某些东西。例如,考虑一个具有以下方法的构建器...

setName()
setType()
setOption()

建造者也可以与抽象工厂相结合,在该工厂中,主管要求工厂提供具体产品。同样,这可以通过将 args 列表传递给工厂来仅使用工厂模式来完成,但是如果 args 是来自服务器或客户端的消息呢?

有时,一种模式的美感比另一种模式的美感更适合。

于 2009-12-22T15:16:07.980 回答
2

Builder 对配置很有用。当您不希望调用者关心实际实现时,抽象工厂很有用。两种不同的目的。

事实上,您可以混合使用这两种模式。例如,Java 的DocumentBuilderFactory是一个经典的抽象工厂:如果您阅读文档,您会看到它用于选择实现的过程。但是,它生成的DocumentBuilder可以在之后进行配置(尽管它们不遵循让每个配置方法返回正在配置的对象的原型“构建器”方法)。

于 2009-12-22T15:10:16.307 回答
2

Builder 是一种控制反转,就像模板方法模式一样。具体构建器上的各种功能是流程中的可自定义步骤。抽象工厂是“公正的”多态性,其输出恰好是一个新构造的对象。混凝土工厂的各种功能是不同的可定制过程,每个过程可能会产生不同的对象。

因此,当您需要有一个共同的整体“情节”来构建您的对象时,您需要一个构建器,但该情节中的各个“场景”是不同的。当不同的实现之间不一定有任何共同点时,您需要一个工厂。

我认为你可以想象一下同时使用这两种方法,抽象工厂充当 Builder 模式中的主管,而每个具体的工厂都以不同的方式指导事情。但是,这超出了 UML 图的“餐巾纸”限制;-)

于 2009-12-22T17:06:05.200 回答
1

当我需要构建多个子工厂来创建不同类型的对象时,我会使用抽象工厂。这些子工厂都实现或扩展了抽象工厂。当只有一种类型的对象需要构建时,我会使用 Builder。

我不确定这是否是正确的用法,但这是我所看到的,以及我过去是如何做到的。

于 2009-12-22T15:10:36.977 回答
1

让我们考虑一个类比。

你在一家快餐连锁店,你实际得到的快餐是相当复杂的。它不仅复杂,而且只有某些人可以为您订购某些类型的食物。现在,你不知道,因为商店经理决定让一个人在一个收银机后面。但如果你仔细看他,它实际上是一个高级全息图,因为它是“抽象的”。一个抽象的订单接受者。现在,当您与全息图交谈并下订单时,全息图计算机会将订单重新路由(多态性)到人类订单接受者大脑中的植入物。植入物使人类实际上按顺序打到一个真实的寄存器。这些“具体”很少被全息图指挥的受精神控制的人类。现在,当真人按订单打卡时,它会被发送到后面的计算机,那里有一条装配线,或“建造者”,用于生产每种类型的食物。根据人和他的收银机,订单被发送到不同类型的装配线。

所以,全息图是抽象的工厂,收银机的精神控制的人是具体的工厂,流水线是建造者。这是一种您可以实际可视化使用构建器模式工作的抽象工厂流程的方式。有时最好看看设计模式如何协同工作,以了解它们有何不同。

如果您正在寻找额外的功劳,请尝试使用泛型来实现这一点。它不需要您想象的那么多代码。

于 2009-12-24T16:31:07.817 回答
0

有关何时使用构建器模式及其优势的更多信息,您应该在此处查看我的帖子以了解另一个类似问题

于 2009-12-23T18:43:07.503 回答
0

在实践中,您可能会使用 DI + IOC 来完成构建者过去所做的事情。供参考。

于 2011-01-27T22:29:45.500 回答
0

好的答案,想添加我的,希望我发现我的理解是正确的。

如果您有一个新办公室,并且只需要新办公桌,您将使用 Builder。一张桌子永远是一张桌子,但一张桌子有许多不同的配置,即大小、形状、厨房的存在、抽屉等。

如果你有一个新办公室,而且里面完全空荡荡的,你会需要各种各样的物品,并且会使用一个抽象工厂来创造家具,我们的办公桌就是从这里下来的。在这种情况下,我们所知道的 Desk 是一个“复杂”对象,因此它使用了 Builder 模式,但在这里它是指导抽象工厂的一部分。抽象工厂还将通过 StraightFunkyLampFactory 类创建简单的对象,例如非常特定的灯 (StraightFunkyLamp)。

于 2013-03-07T23:03:01.113 回答