java 构建器模式与配置对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3394853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Builder pattern vs. config object
提问by deamon
The builder pattern is popular to create immutable objects, but there is some programming overhead to create a builder. So I wonder why not simply using a config object.
构建器模式在创建不可变对象时很流行,但是创建构建器需要一些编程开销。所以我想知道为什么不简单地使用一个配置对象。
The usage of a builder would look like this:
构建器的用法如下所示:
Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();
It is obvious that this is very readable and concise, but you have to implement the builder:
很明显,这是非常可读和简洁的,但是您必须实现构建器:
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);
}
}
}
My idea is, to reduce the code by using a simple config object like 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;
}
}
Usage:
用法:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
This usage needs a few more lines but is also very readable, but the implementation is much simpler and maybe it is easier to understand for someone who isn't familiar with the builder pattern. By the way: is there a name for this pattern?
这种用法需要多几行代码,但也非常易读,但实现要简单得多,对于不熟悉构建器模式的人来说,可能更容易理解。顺便说一句:这个模式有名字吗?
Is there a drawback in the config approach that I've overlooked?
我忽略的配置方法有什么缺点吗?
采纳答案by Martin Hutchinson
The builder pattern improves decoupling - your Product can be an interface and the only class that knows about the implementation (or implementations, in some cases) is the builder. If the builder also implements an interface then you can inject this into your code to increase decoupling further.
构建器模式改进了解耦——你的产品可以是一个接口,唯一知道实现(或在某些情况下实现)的类是构建器。如果构建器还实现了一个接口,那么您可以将其注入到您的代码中以进一步增加解耦。
This decoupling means your code is more maintainable and easier to test.
这种解耦意味着您的代码更易于维护且更易于测试。
回答by NoozNooz42
You are losing several advantages of the builder pattern, as has already been pointed out (newis ugly and harder to maintain and leaking details compared to a clean builder).
正如已经指出的那样,您正在失去构建器模式的几个优点(与干净的构建器相比,new丑陋且难以维护和泄漏细节)。
The one I miss the most however is that the builder pattern can be used to provide what are called "fluent interfaces".
然而,我最怀念的是构建器模式可用于提供所谓的“流畅接口”。
Instead of this:
取而代之的是:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
You can do:
你可以做:
ProductFactory.create()
.drink("Vodka")
.whereAlcohoolLevelIs(0.38)
.inABottleSized(0.7)
.pricedAt(17.99)
.build();
Not everyone like fluent interfaces, but they are definitely a very nice use of the builder pattern (all fluent interfaces should use the builder pattern, but not all builder pattern are fluent interfaces).
不是每个人都喜欢 fluent 接口,但它们绝对是 builder 模式的一个很好的使用(所有 fluent 接口都应该使用 builder 模式,但不是所有的 builder 模式都是 fluent 接口)。
Some great Java collections, like the Google collections, makes both very liberal and very good use of "fluent interfaces". I'd pick these any day over your "easier-to-type/less characters"approach : )
一些很棒的 Java 集合,例如 Google 集合,非常自由且非常好地使用了“流畅的接口”。我会在任何一天通过你的“更容易输入/更少字符”的方法来选择这些:)
回答by atamanroman
What problem do you try to solve with your pattern? The builder pattern is used for objects with many (optional) parameters in order to prevent tons of different constructors or very long ones. It also keeps your object in a consistent state (vs. javabean pattern) during construction.
你试图用你的模式解决什么问题?构建器模式用于具有许多(可选)参数的对象,以防止大量不同的构造函数或很长的构造函数。它还使您的对象在构造过程中保持一致状态(相对于 javabean 模式)。
The difference between builder and "config object" (feels like a good name) is, that you still have to create the object with the same params by constructor or getter/setter. This a) doesnt solve the constructor problem or b) keeps the config object in inconsistent state. Inconsistent states of the config object dont really hurt it but you could pass an unfinished config object as a param. [Michids link to phantom types seem to solve this problem, but that again kills readability (new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>kinda sucks).]Thats the big advantage of the builder pattern: you can validate your params before you create an object andyou can return any subtype (acts like a factory).
builder 和“config object”(感觉是个好名字)之间的区别在于,您仍然必须通过构造函数或 getter/setter 创建具有相同参数的对象。这 a) 不能解决构造函数问题或 b) 使配置对象保持不一致状态。配置对象的不一致状态并没有真正伤害它,但您可以将未完成的配置对象作为参数传递。[Michids 链接到幻像类型似乎解决了这个问题,但这又会降低可读性(new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>有点糟糕)。]这就是构建器模式的一大优势:您可以在创建对象之前验证您的参数,并且您可以返回任何子类型(行为像一个工厂)。
Config objects are valid for sets of params which are all mandatory. Ive seen this pattern many times in .NET or java before.
配置对象对所有必需的参数集都是有效的。我以前在 .NET 或 java 中多次看到这种模式。
回答by Michael Lloyd Lee mlk
Have you considered using builder-builder?
您是否考虑过使用builder-builder?
I do think the builder (with prefixes like "With") reads more natrually/fluently.
我确实认为构建器(带有像“With”这样的前缀)读起来更自然/流畅。
回答by naikus
IMO, the builder pattern is much more roboust if you have things like validation, etc.
IMO,如果您有验证等功能,构建器模式会更加健壮。
The builder pattern in your case can be changed to do the following:
您的案例中的构建器模式可以更改为执行以下操作:
Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
The build()method can do all the validation stuff that is needed for your builder.
The builder pattern also has several design advangates (all of which may not be applicable in your case). For deatils check this question
该build()方法可以完成构建器所需的所有验证工作。构建器模式还具有几个设计优势(所有这些优势可能不适用于您的情况)。有关详细信息,请检查此问题
回答by Lawrence
I personally feel that the builder pattern at first sight offers you cleaner code where these objects are in fact used. On the other hand, not having getters/setters will not be very usable by a lot of frameworks that expect camel case getters/setters. This is a serious draw back I feel.
我个人认为,一见钟情的构建器模式为您提供了更清晰的代码,其中实际上使用了这些对象。另一方面,没有 getter/setter 对许多期望驼峰式 getter/setter 的框架不太有用。我觉得这是一个严重的退步。
What I also like with getters/setters is that you clearly see what you are doing: get or set. I feel with the builder I am losing a bit of intuitive clarity here.
我还喜欢 getter/setter 的一点是你清楚地看到你在做什么:get 或 set。我觉得对于构建器,我在这里失去了一些直观的清晰度。
I know that many people have read a specific book and that now all of a sudden, the builder pattern has enjoyed the hype, as if it were the new iPhone. However, I am not an early adopter. I only use "the new way" when it really really proves a big time saver on whatever territory, being it performance, maintenance, coding...
我知道很多人都读过一本特定的书,现在突然之间,构建器模式受到了大肆宣传,就好像它是新 iPhone 一样。但是,我不是早期采用者。我只在“新方式”真的证明在任何领域都可以节省大量时间时才使用它,无论是性能、维护、编码......
My hands-on experience is that I usually am better of with getters/setters and constructors. It allows me to reuse these POJO's for any purpose.
我的实践经验是,我通常更擅长使用 getter/setter 和构造函数。它允许我为任何目的重用这些 POJO。
Although I see the purpose of your Config Object, I also think it is even more overhead than the builder, and for what? What is wrong with setters?
虽然我看到你的Config Object的目的,但我也认为它比builder的开销更大,为了什么?setter 有什么问题?
Maybe we need to invent a WITH clause: example, let's say you have
也许我们需要发明一个 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();
}
}
Ah I dunno... I think this may yet result in quicker code writing without all the inner class hassle. Does any one have connections with the Oracle Java guru's?
啊,我不知道......我认为这可能会导致更快的代码编写,而无需所有内部类的麻烦。有人与 Oracle Java 大师有联系吗?
It doesn't look as clean as using an object with a builder, but you save builder-construction time. And you can still use the class as a regular pojo/bean which can be used in frameworks...
它看起来不像使用带有构建器的对象那么干净,但是您可以节省构建器构建时间。而且您仍然可以将该类用作可以在框架中使用的常规 pojo/bean...
Would you guys actually like this clause or you think it would rather suck? Cheers
你们是真的喜欢这个条款还是你认为它更糟糕?干杯
回答by TimCrowe
The configuration pattern and the builder pattern are functionally equivalent. They both solve the same problems -
配置模式和构建器模式在功能上是等效的。他们都解决了同样的问题——
Eliminate the need for multiple constructor signatures
Allow fields to only be set during construction
Allow consumers to only set values they care about and have logical defaults for the other values
消除对多个构造函数签名的需要
允许字段仅在构建期间设置
允许消费者只设置他们关心的值,并为其他值设置逻辑默认值
Anything you want to do in one of these patterns you can do in the other, such as only allowing state to be set with methods that do validation and setting state with encapsulated logic. The only real difference is if you like creating objects with the newkey term or if you like calling a .build()method.
您想在其中一种模式中执行的任何操作都可以在另一种模式中执行,例如仅允许使用执行验证的方法设置状态并使用封装的逻辑设置状态。唯一真正的区别是您是否喜欢使用new关键字创建对象,或者您是否喜欢调用.build()方法。
回答by irreputable
The main drawback is that it is not in Joshua's book, so drones can't wrap their heads around it.
主要的缺点是它不在 Joshua 的书中,因此无人机无法将头环绕在它周围。
You are using a simple value object to hold multiple arguments a function(/method/constructor) needs, there is nothing wrong with that, it has been done for ages. As long as we don't have named optional parameters we'll have to devise workarounds like this - it's a shame, not some god damn brilliant inventions from the gods in the Sun.
您正在使用一个简单的值对象来保存函数(/方法/构造函数)需要的多个参数,这并没有什么问题,这已经做了很多年了。只要我们没有命名可选参数,我们就必须设计像这样的变通方法 - 很遗憾,不是来自太阳神的一些该死的辉煌发明。
The real difference is that you expose fields directly. Joshua would never have a public mutable field - but he writes APIs that will be used by millions of people most of which are morons, and the APIs must be safe to evolve for decades to come, and they can allocate many man months just to design a simple class
真正的区别在于您直接公开字段。Joshua 永远不会有一个公开的可变字段——但他编写的 API 将被数百万人使用,其中大多数是白痴,而且这些 API 必须在未来几十年内安全发展,他们可以分配许多人月来设计一个简单的类
Who are we to emmulate that?
我们有谁来效仿呢?
回答by Damian Leszczyński - Vash
You should not use the public field, but protected or private. For accesing then you should use the getters and setter to keep the encapsulation..
您不应使用公共字段,而应使用 protected 或 private。对于访问,您应该使用 getter 和 setter 来保持封装..

