你会在哪里使用Builder模式而不是抽象工厂?

时间:2009-12-22 15:04:05

标签: language-agnostic design-patterns builder-pattern

我已经看到这个问题在这里和那里出现了几次,但我从来没有找到并回答我很满意。

来自维基百科:

  

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

但对客户来说不是一回事吗?一旦它被构建,他就会获得完整的对象,所以对他来说没有额外的功能。

我看到它的唯一方法是作为一种方式或按步骤组织构造函数代码,强制构造实现构建器。这很好,但离抽象工厂几乎没有什么好处。


维基百科的下一篇文章是一个很好的参考,以达到我的观点:

  

通常,设计开始使用工厂方法(不太复杂,可定制,子类增加),并逐渐向抽象工厂,原型或构建器发展(更灵活,更复杂),因为设计师发现需要更多灵活性。

如果是这样,那么您需要在系统中引入哪种复杂性,从抽象工厂更改为构建器?

我的观点是,我无法找到并举例说明抽象工厂是否足够,而你需要一个生成器。

10 个答案:

答案 0 :(得分:13)

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

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


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

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

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


更新以回应此评论:

  

Lino>尽管如此,它仍然困扰着我。我的意思是,如果你正在构建复合对象,并且你把导演拿出来,将调用构建器方法的责任传递给客户端,那么它对我有用。只有这样,建筑师才能完美契合。但是对于导演来说,它看起来并不多......

准确地说,Builder没有导演,它允许每个客户根据其特定需求创建复杂的产品。客户是董事,因为董事的实施不会被重复使用。

  

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

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

答案 1 :(得分:7)

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

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

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

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

好吧,一个蹩脚的故事,但只是为了减轻麻烦:P

答案 2 :(得分:4)

如果您要根据命令行参数逐个构建某些内容,那么您需要构建器的一个很好的示例。例如,考虑使用类似......

等方法的构建器
setName()
setType()
setOption()

建筑商也可以与抽象工厂合并,导演要求工厂提供具体产品。同样,这可以通过将args列表传递给工厂只使用工厂模式来完成,但是如果args是来自服务器或客户端的消息,那该怎么办呢。

有时,一种模式的美学恰好比另一种模式的美学更合身。

答案 3 :(得分:2)

Builder对配置很有用。当您不希望调用者关心实际实现时,Abstract Factory非常有用。两个不同的目的。

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

答案 4 :(得分:2)

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

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

我认为你可以想象一下,使用抽象工厂作为Builder模式中的导演,以及每个具体工厂以不同方式指导事物。但是,这超出了UML图的“餐巾”限制; - )

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

让我们考虑类比。

你在快餐连锁店,你实际吃的快餐非常复杂。它不仅复杂,而且只有某些人可以为某些类型的食物订购。现在,你不知道,因为商店的经理决定把一个人放在一个收银台后面。但如果你仔细看他,那它实际上是一个先进的全息图,因为它是“抽象”。抽象的订单接受者。现在,当你与全息图交谈,并下订单时,全息图计算机重新路由(多态),以便在人类订单接收者的大脑中植入。植入物使人类实际按顺序冲到真正的寄存器。这些“具体的”精神控制的人类很少被全息图命令。现在,当真正的人类按顺序进行打击时,它会被发送到后面的计算机,其中有一条装配线,或者“builder”,用于构建每种类型的食物。订单根据人和他的收银机发送到不同类型的装配线。

因此,全息图是抽象的工厂,登记处的精神控制的人是具体的工厂,装配线是建造者。这是一种实际可视化工作 WITH 构建器模式的抽象工厂流的方法。有时最好看看设计模式如何协同工作,以便了解它们之间的差异。

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

答案 7 :(得分:0)

有关何时使用构建器模式及其优点的详细信息,请查看我的帖子,了解其他类似问题 here

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

如果你有一个新的办公室,只需要新的办公桌,你就可以使用一个Builder。桌子总是一张桌子,但桌子有许多不同的配置,即尺寸,形状,储物柜,抽屉等等。

如果你有一个新的办公室,里面是完全裸露的,你将需要各种物品,并使用一个抽象工厂来创建家具,我们的桌子从这里下降。在这种情况下,我们知道Desk是一个“复杂”对象,所以它使用Builder模式,但在这里它是一个指向抽象工厂的一部分。抽象工厂还可以通过StraightFunkyLampFactory类创建简单的对象,如非常特定的灯(StraightFunkyLamp)。