流畅的界面和 Builder 模式有啥区别?
Posted
技术标签:
【中文标题】流畅的界面和 Builder 模式有啥区别?【英文标题】:What is the difference between a fluent interface and the Builder pattern?流畅的界面和 Builder 模式有什么区别? 【发布时间】:2013-07-30 01:15:58 【问题描述】:我对设计模式非常陌生,并且对 fluent interfaces 和 Builder 模式之间的区别感到困惑。
我了解流畅界面的概念。但是构建器模式有点令人困惑。我无法理解在 Builder 模式中使用 Director。
我可以同时使用 Builder 模式和 Fluent Interface 吗?如果是这样,那么我应该如何与导演和混凝土建筑商一起这样做?
我的问题不是关于构建器模式的优点。但是这个问题的目的是了解构建器模式和流畅界面之间的关系。
使用来自 GoF 的 Builder 的 UML 序列图进行编辑:
【问题讨论】:
我可以同时使用 Builder 模式和 Fluent Interface 吗? 可以,here 发布了一个很好的例子 更多示例在***.com/questions/328496/…。 我能知道这个问题与上述问题的重复吗?因为它是关于流畅界面和构建器模式之间的关系。 我不确定这个问题是否在副本中得到了正确回答。许多人错误地引用了 GoF Builder。 GoF 的 True Builder 允许扩展(添加)ConcreteBuilder
类,而无需修改 Client
类或 Director
类。请参阅我添加到您的问题中的序列图。方法链或流式接口不是 GoF 的 Builder。 GoF 中的示例根本不使用方法链。
我发现以下内容有助于找出差异。这里作者将builder模式版本1和版本2称为fluent Interfaceblog.crisp.se/2013/10/09/perlundholm/…
【参考方案1】:
Fluent Interfaces 是语义外观。您将它们放在现有代码之上,以减少语法噪音并更清楚地表达代码在通用语言中的作用。这是构建内部域特定语言时使用的模式。这是关于可读性的。
导演/建造者协调某物的建造。也就是说,如果您正在构建披萨烘焙机,Director 将确保从订单到披萨的步骤由正确的构建者使用正确的数据以正确的顺序执行。这是关于验证和授权的。
您当然可以在 Director/Builder 模式之上放置一个 Fluent Interface 以使其更流畅地阅读并强调领域概念(相对于构建和委派的技术过程)。那可能是Expression Builder。
我想强调的是,Fluent Interfaces 不仅仅是Method Chaining。这是一个常见的误解。方法链是实现流畅接口的一种方法,但它并不相同,因为它缺乏语义质量,例如这不是一个流畅的界面:
SomeObject.setFoo(1).setBar(2).setBaz(3);
以上内容并没有表达任何关于 SomeObject 的内容。它不是某些语义模型之上的外观。这只是一些链接的方法。 Fluent Interface 的一个示例是 SQL 查询构建器,例如
SQLBuilder.select('foo').from('bar').where('foo = ?', 42).prepare();
在该 API 的底层是创建 SQL 语句的代码。它可能包含多个对象,显示的调用可以很好地创建一个 Select 对象,调用它的 setter,创建一个 Condition 对象并将其应用于 Select 对象,最后返回一个 Statement 对象。但是这一切对我们都是隐藏的。这也突出了 Fluent Interfaces 的另一个方面:它们可能违反 SOLID 和 Law of Demeter。但由于它是代码之上的外观,希望遵循这些设计原则,所以没那么重要,因为您将违规定位到 Fluent 接口。
【讨论】:
如果代码执行var foo = SQLBuilder.select('foo').from('bar'); var bar=foo.where(expr1,42); foo.where(exp2,42).prepare(); bar.prepare()
,语义是什么? Fluent 接口对中间结果的可变性或不变性有什么看法?我的理解是没有任何严格的约定,但也许我弄错了?
@supercat 您的 SQLBuilder 将从任何调用中返回 itself 的实例,因此您的 foo 和您的 bar 都引用构建器。只有当您调用 prepare 时,您才能完成构建过程并获得准备好的语句对象。
我认为您的 SQLBuilder 和 SomeObject 之间没有区别。它们中的每一个都是简单的方法链接。我的观点是流畅的接口只是方法链的一种模式。最酷的是当它们被构建为利用 IDE 时,通过返回一个仅支持从那时起允许的操作的对象。您还说方法链接是一种方法,但我认为这是错误的,没有方法链接的流畅接口根本不流畅。
@Gordon 从技术上讲,他创造了这个词,所以我猜他是参考。然而,我仍然认为他把他的大部分东西都卖得太多了。 Fluent 是有效的方法链接改进。我说的是smalltalk speak中的链接,你在返回另一个方法时调用一个方法,所以你可以跳过一个局部变量。现在如果你使用这个特性来构建一个聪明的 DSL,你就很流利了。问题是,“聪明”是有争议的。你的两个例子都很流畅,一个更聪明,因此感觉更流畅。但是如果不流利,第一个是什么?
@Gordon 因此,在我看来,语义并不是使接口流畅的原因,因为您可以使用语句和分号轻松地使用与传统 OOP 样式接口相同的语义。正如 Dave Fancher 所说,我将 fluent 定义为:“流畅的接口是一种专门的、自引用的方法链接形式,其中上下文通过链进行维护。”【参考方案2】:
Fluent 接口背后的理念是,可以通过用点连接对象来将多个属性应用于对象,而不必每次都重新指定对象。构建器模式背后的想法是,非共享的可变对象通常比非共享的不可变对象更容易使用,但与共享可变对象相比,共享不可变对象更容易推理。因此,代码可以使用一个易于使用的可变对象来生成所需实例的“模型”,然后使用它来制作一个易于共享的、包含相同数据的不可变对象。
这两个想法可以很好地协同工作,但有些正交。
请注意,流畅的界面至少可以通过三种方式工作:
通过让实例的每个成员返回一个应用了适当更改的新实例。 通过让每个成员改变调用它的实例并返回它。 通过让每个成员返回一个轻量级补丁对象的实例,该对象包含指向正在修改的事物或前一个补丁的链接。最后一种样式需要采取一些措施来应用所有补丁,但如果要修改的对象很大并且需要进行许多更改,它可以最大限度地减少所需的复制量。
【讨论】:
不确定我是否同意您对 Fluent Interface 的定义。听起来只是方法链接。我不会投反对票,但请参阅我的答案以了解 Fluent Interfaces 实现了什么。 -1 由于流畅接口的描述不正确/不完整,几乎不相关的不可变/可变对象具有构建器模式和不正确的正交性声明。当然是国际海事组织。 不知道为什么人们批评你的回答,你很准确。 Fluent 是一种模式,用于将方法级联添加到没有它的语言中,并将级联与链接相结合。我也没有看到对构建器的批评,可变性/不变性是它的主要用例之一。虽然它可以用于更多的事情,比如跨方法聚合参数,以及进行验证等。 Didier,有几个静态方法来创建对象而不是构建器有什么问题?我正在尝试确定我是否真的需要一个构建器来构建只有一个或两个构造序列的流利对象。以上是关于流畅的界面和 Builder 模式有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
Flutter在Debug和Release下分别使用啥编译模式,有啥区别?
Flash Professional 和 Flash Builder 之间有啥区别?