简体   繁体   English

Builder模式与配置对象

[英]Builder pattern vs. config object

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? 配置方法有缺点我忽略了吗?

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. 构建器模式改进了解耦 - 您的Product可以是一个接口,唯一知道实现(或在某些情况下实现)的类是构建器。 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. 这种解耦意味着您的代码更易于维护且更易于测试。

You are losing several advantages of the builder pattern, as has already been pointed out ( new is ugly and harder to maintain and leaking details compared to a clean builder). 正如已经指出的那样,你正在失去构建器模式的几个优点(与干净的构建器相比, 的难看并且难以维护和泄漏细节)。

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). 并不是每个人都喜欢流畅的界面,但它们绝对是对构建器模式的非常好用(所有流畅的接口都应该使用构建器模式,但并非所有构建器模式都是流畅的接口)。

Some great Java collections, like the Google collections, makes both very liberal and very good use of "fluent interfaces" . 一些优秀的Java集合,如Google集合,使得“流畅的接口”非常自由和非常好用。 I'd pick these any day over your "easier-to-type/less characters" approach : ) 我会在你的“更容易打字/更少字符”的方法中选择这些:)

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. 构建器和“配置对象”(感觉就像一个好名字)之间的区别在于,您仍然需要通过构造函数或getter / setter创建具有相同参数的对象。 This a) doesnt solve the constructor problem or b) keeps the config object in inconsistent state. 这a)不解决构造函数问题或b)使配置对象保持不一致状态。 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 and you can return any subtype (acts like a factory). [Michids链接到幻像类型似乎解决了这个问题,但这再次杀死了可读性( new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>有点糟糕)。]这就是构建器模式的一大优势:你可以验证你的params在创建对象之前您可以返回任何子类型(就像工厂一样)。

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中多次看过这种模式。

Have you considered using builder-builder ? 您是否考虑过使用构建器构建器

I do think the builder (with prefixes like "With") reads more natrually/fluently. 我认为构建器(带有“With”之类的前缀)更自然/流利地读取。

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. 另一方面,没有getter / setter将不会被许多期望camel case getter / setter的框架所使用。 This is a serious draw back I feel. 我觉得这是一个严重的退缩。

What I also like with getters/setters is that you clearly see what you are doing: get or set. 我也喜欢getter / setter,你清楚地看到你在做什么:得到或设置。 I feel with the builder I am losing a bit of intuitive clarity here. 我觉得这位建筑师在这里失去了一点直观的清晰度。

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. 我知道很多人已经阅读了一本特定的书,现在突然之间,建设者模式已经享受了炒作,好像它是新的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... 我只使用“新方式”,它真正证明了在任何领域都能节省大量时间,无论是性能,维护,编码......

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. 它允许我将这些POJO用于​​任何目的。

Although I see the purpose of your Config Object, I also think it is even more overhead than the builder, and for what? 虽然我看到了Config对象的用途,但我也认为它比构建器更具开销,为什么呢? What is wrong with setters? 安装者有什么问题?

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 干杯

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. build()方法可以完成构建器所需的所有验证工作。 The builder pattern also has several design advangates (all of which may not be applicable in your case). 构建器模式还有几个设计方案(所有这些都可能不适用于您的情况)。 For deatils check this question 对于deatils,请检查此问题

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 new key term or if you like calling a .build() method. 唯一真正的区别是,如果您喜欢使用new关键术语创建对象,或者您喜欢调用.build()方法。

The main drawback is that it is not in Joshua's book, so drones can't wrap their heads around it. 主要的缺点是它不在约书亚的书中,因此无人机无法绕过它。

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. 你正在使用一个简单的值对象来保存函数(/ method / constructor)需要的多个参数,没有任何问题,它已经做了很多年。 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 约书亚永远不会有一个公共的可变领域 - 但他写的API将被数百万人使用,其中大部分是蠢货,而且API必须安全地发展到未来几十年,并且他们可以分配许多个月只是为了设计一个简单的课程

Who are we to emmulate that? 我们是谁来哄骗那个?

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.. 对于访问,你应该使用getters和setter来保持封装。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM