构建器模式很受欢迎,可用于创建不可变对象,但创建构建器需要一些编程开销。所以我想知道为什么不简单地使用配置对象。
构建器的用法如下所示:
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);
这种用法需要更多行,但也非常易读,但实现起来要简单得多,也许对于不熟悉构建器模式的人来说更容易理解。顺便说一下:这个模式有名字吗?
我忽略了配置方法的缺点吗?
答案 0 :(得分:16)
构建器模式改进了解耦 - 您的Product可以是一个接口,唯一知道实现(或在某些情况下实现)的类是构建器。如果构建器还实现了一个接口,那么您可以将其注入代码中以进一步增加解耦。
这种解耦意味着您的代码更易于维护且更易于测试。
答案 1 :(得分:6)
您正在失去构建器模式的几个优点,正如已经指出的那样(与干净的构建器相比, new 更难以维护和泄漏细节)。
我最想念的那个是构建器模式可以用来提供所谓的“流畅的接口”。
而不是:
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集合,可以非常自由地使用“流畅的界面”。我会在“更容易输入/更少字符”的方法中选择这些:)
答案 2 :(得分:4)
您尝试使用模式解决什么问题?构建器模式用于具有许多(可选)参数的对象,以防止大量不同的构造函数或非常长的构造函数。它还可以在构造期间使对象保持一致状态(与javabean模式相对)。
构建器和“配置对象”(感觉就像一个好名字)之间的区别在于,您仍然需要通过构造函数或getter / setter创建具有相同参数的对象。这a)不解决构造函数问题或b)使配置对象保持不一致状态。配置对象的不一致状态不会真正伤害它,但您可以将未完成的配置对象作为参数传递。 [与幻象类型的Michids链接似乎可以解决这个问题,但这再次杀死了可读性(new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>
有点糟糕)。] 这就是构建器模式的一大优势:你可以在之前验证你的参数你创建一个对象和你可以返回任何子类型(就像工厂一样)。
Config对象对于所有必需的参数集都有效。我以前在.NET或java中多次看过这种模式。
答案 3 :(得分:2)
您是否考虑过使用builder-builder?
我确实认为构建器(带有“With”之类的前缀)更自然/流利地读取。
答案 4 :(得分:1)
IMO,如果你有验证等等,那么构建器模式会更加强大。
您的案例中的构建器模式可以更改为执行以下操作:
Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
build()
方法可以完成构建器所需的所有验证工作。
构建器模式还有几个设计方案(所有这些都可能不适用于您的情况)。对于deatils,请检查此question
答案 5 :(得分:1)
我个人认为构建器模式第一眼就能为您提供更清晰的代码,而这些代码实际上是在这些代码中使用的。另一方面,没有getter / setter将不会被许多期望camel case getter / setter的框架所使用。这是我觉得的严重缺点。
我对getter / setter的喜欢之处在于你清楚地看到你在做什么:获取或设置。我觉得这位建筑师在这里失去了一点直觉的清晰度。
我知道很多人都读过一本特定的书,现在突然之间,建设者模式已经享受了炒作,好像它是新的iPhone。但是,我不是早期采用者。我只使用“新方式”,它真正证明了在任何领域都能节省大量时间,无论是性能,维护,编码......
我的实践经验是,我通常更喜欢吸气/安装和施工人员。它允许我将这些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 ...
你们真的喜欢这个条款还是你认为它会更糟糕? 干杯
答案 6 :(得分:1)
配置模式和构建器模式在功能上是等效的。他们都解决了同样的问题 -
消除对多个构造函数签名的需求
仅允许在构造期间设置字段
允许消费者只设置他们关心的值,并为其他值设置逻辑默认值
您可以在其中一种模式中执行任何操作,例如,只允许使用执行验证的方法设置状态,并使用封装逻辑设置状态。唯一真正的区别在于,如果您喜欢使用new
关键词创建对象,或者您想要调用.build()
方法。
答案 7 :(得分:0)
主要的缺点是它不在约书亚的书中,因此无人机无法绕过它。
你正在使用一个简单的值对象来保存函数(/ method / constructor)需要的多个参数,没有任何问题,它已经做了很多年。只要我们没有命名可选参数,我们就必须设计这样的解决方法 - 这是一种耻辱,而不是来自太阳神灵的那些神奇的发明。
真正的区别在于您直接暴露字段。约书亚永远不会有一个公共可变领域 - 但他写的API将被数百万人使用,其中大多数是蠢货,而且API必须安全地发展到未来几十年,他们可以分配许多人月只是设计一个简单的类
我们是谁来制造这个?
答案 8 :(得分:-1)
您不应使用公共字段,而应使用受保护或私有字段。为了访问,你应该使用getters和setter来保持封装..