Builder模式与配置对象

构建器模式在创建不可变对象时很流行,但是创建构建器会产生一些编程开销。 所以我想知道为什么不简单地使用配置对象。

构建器的用法如下所示:

Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build(); 

很明显,这是非常易读和简洁的,但您必须实现构建器:

 public class Product { public final String name; public final float alcohol; public final float size; public final float price; private Product(Builder builder) { this.name = builder.name; this.alcohol = builder.alcohol; this.size = builder.size; this.price = builder.price; } public static class Builder { private String name; private float alcohol; private float size; private float price; // mandatory public static Builder name(String name) { Builder b = new Builder(); b.name = name; return b; } public Builder alcohol(float alcohol) { this.alcohol = alcohol; return.this; } public Builder size(float size) { this.size = size; return.this; } public Builder price(float price) { this.price = price; return.this; } public Product build() { return new Product(this); } } } 

我的想法是,通过使用这样的简单配置对象来减少代码:

 class ProductConfig { public String name; public float alcohol; public float size; public float price; // name is still mandatory public ProductConfig(String name) { this.name = name; } } public class Product { public final String name; public final float alcohol; public final float size; public final float price; public Product(ProductConfig config) { this.name = config.name; this.alcohol = config.alcohol; this.size = config.size; this.price = config.price; } } 

用法:

 ProductConfig config = new ProductConfig("Vodka"); config.alcohol = 0.38; config.size = 0.7; config.price = 17.99; Product p = new Product(config); 

这种用法需要更多的行,但也非常易读,但实现起来要简单得多,也许对于不熟悉构建器模式的人来说更容易理解。 顺便说一下:这个模式有名字吗?

我忽略了配置方法的缺点吗?

构建器模式改进了解耦 – 您的Product可以是一个接口,唯一知道实现(或在某些情况下实现)的类是构建器。 如果构建器还实现了接口,那么您可以将其注入代码中以进一步增加解耦。

这种解耦意味着您的代码更易于维护且更易于测试。

正如已经指出的那样,你正在失去构建器模式的几个优点(与干净的构建器相比, 的难看并且难以维护和泄漏细节)。

然而,我最想念的是构建器模式可用于提供所谓的“流畅接口”

而不是这个:

 ProductConfig config = new ProductConfig("Vodka"); config.alcohol = 0.38; config.size = 0.7; config.price = 17.99; Product p = new Product(config); 

你可以做:

 ProductFactory.create() .drink("Vodka") .whereAlcohoolLevelIs(0.38) .inABottleSized(0.7) .pricedAt(17.99) .build(); 

并非每个人都喜欢流畅的界面,但它们绝对是对构建器模式的非常好用(所有流畅的接口都应该使用构建器模式,但并非所有构建器模式都是流畅的接口)。

一些优秀的Java集合,如Google集合,使得非常自由和非常好地使用“流畅的接口” 。 我会在你的“更容易打字/更少字符”的方法中选择这些方法:)

你试图用你的模式解决什么问题? 构建器模式用于具有许多(可选)参数的对象,以防止大量不同的构造函数或非常长的构造函数。 它还可以在构造期间使对象保持一致状态(与javabean模式对比)。

构建器和“配置对象”(感觉就像一个好名字)之间的区别在于,您仍然需要通过构造函数或getter / setter创建具有相同参数的对象。 这a)不解决构造函数问题或b)使配置对象保持不一致状态。 配置对象的不一致状态不会真正伤害它,但您可以将未完成的配置对象作为参数传递。 [Michids链接到幻像类型似乎解决了这个问题,但这再次杀死了可读性( new Foo有点糟糕)。]这就是构建器模式的一大优势:你可以validation你的params在创建对象之前您可以返回任何子类型(就像工厂一样)。

配置对象对于所有必需的参数集都有效。 我以前在.NET或java中多次看过这种模式。

IMO,如果您有validation等内容,那么构建器模式就更容易被破坏了。

您的案例中的构建器模式可以更改为执行以下操作:

 Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build(); 

build()方法可以完成构建器所需的所有validation工作。 构建器模式还有几个设计方案(所有这些都可能不适用于您的情况)。 对于deatils,请检查此问题

您是否考虑过使用构建器构建器 ?

我确实认为构建器(带有“With”之类的前缀)更自然/流利地读取。

我个人认为,第一眼看到的构建器模式为您提供了更清晰的代码,而这些代码实际上已经被使用了。 另一方面,没有getter / setter将不会被许多期望camel case getter / setter的框架所使用。 这是我觉得的严重缺点。

我也喜欢getter / setter,你清楚地看到你在做什么:得到或设置。 我觉得这个建筑师我在这里失去了一些直观的清晰度。

我知道很多人已经阅读了一本特定的书,现在突然之间,建设者模式已经享受了炒作,好像它是新的iPhone。 但是,我不是早期采用者。 我只使用“新方式”才能真正certificate在任何领域都能节省大量时间,无论是性能,维护,编码……

我的实践经验是,我通常更喜欢吸气者/安装者和施工人员。 它允许我为任何目的重用这些POJO。

虽然我看到了Config对象的用途,但我也认为它比构建器更有开销,为什么呢? 制定者有什么问题?

也许我们需要发明一个WITH子句:例如,假设你有

 public Class FooBar() { private String foo; public void setFoo(String bar) { this.foo = bar; } public String getFoo() { return this.foo; } } public static void main(String []args) { FooBar fuBar = new FooBar(); String myBar; with fuBar { setFoo("bar"); myBar = getFoo(); } } 

啊,我不知道……我认为这可能会导致更快的代码编写而没有内部的所有麻烦。 有没有人与Oracle Java大师有联系?

它看起来不像使用带有构建器的对象那样干净,但是您可以节省构建器构建时间。 你仍然可以使用该类作为常规的pojo / bean,可以在框架中使用…

你们真的喜欢这个条款还是你认为它会吮吸? 干杯

配置模式和构建器模式在function上是等效的。 他们都解决了同样的问题 –

  • 消除了对多个构造函数签名的需要

  • 允许仅在构造期间设置字段

  • 允许使用者仅设置他们关心的值,并具有其他值的逻辑默认值

您可以在其中一种模式中执行任何操作,例如,只允许使用执行validation的方法设置状态,并使用封装逻辑设置状态。 唯一真正的区别在于,如果您喜欢使用new关键术语创建对象,或者您喜欢调用.build()方法。

主要的缺点是它不在约书亚的书中,因此无人机无法绕过它。

你正在使用一个简单的值对象来保存一个函数(/ method / constructor)需要的多个参数,没有任何问题,它已经做了很多年。 只要我们没有命名可选参数,我们就必须设计这样的解决方法 – 这是一种耻辱,而不是来自太阳神灵的一些该死的神奇发明。

真正的区别在于您直接暴露字段。 约书亚永远不会有一个公共的可变领域 – 但他写的API将被数百万人使用,其中大多数是蠢货,API必须安全地发展到未来几十年,并且他们可以分配许多个月只是为了设计一个简单的课程

我们是谁来嘲笑那个?

您不应使用公共字段,而应使用受保护或私有字段。 对于访问,你应该使用getters和setter来保持封装。