在Java中的另一个生成器中使用生成器

在Java中的另一个生成器中使用生成器,java,design-patterns,Java,Design Patterns,在Java中的另一个构建器中使用构建器是一种好的做法,还是最好向“更高”级别的构建器添加方法来在幕后创建对象 new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build()).build(); 如Andy Turner在其评论中所述,在另一个构建中重用构建器逻辑是很好的:您不想重复代码。 但以这种方式执行链接实际上是不可读的: Car car = new Car.CarBuilder

在Java中的另一个构建器中使用构建器是一种好的做法,还是最好向“更高”级别的构建器添加方法来在幕后创建对象

new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build()).build();

如Andy Turner在其评论中所述,在另一个构建中重用构建器逻辑是很好的:您不想重复代码。
但以这种方式执行链接实际上是不可读的:

Car car = new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build()).build();
您可以采用不同的格式,但仍然无法理解:

Car car = 
new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build())
                    .build();   
在局部变量中提取
引擎
实例看起来非常清晰:

Engine engine = new Engine.EngineBuilder().name("Diesel").weight(5).build();
Car car = new Car.CarBuilder().engine(engine).build();

如Andy Turner在其评论中所述,在另一个构建中重用构建器逻辑是很好的:您不想重复代码。
但以这种方式执行链接实际上是不可读的:

Car car = new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build()).build();
您可以采用不同的格式,但仍然无法理解:

Car car = 
new Car.CarBuilder().engine(new Engine.EngineBuilder().name("Diesel").weight(5).build())
                    .build();   
在局部变量中提取
引擎
实例看起来非常清晰:

Engine engine = new Engine.EngineBuilder().name("Diesel").weight(5).build();
Car car = new Car.CarBuilder().engine(engine).build();

我更喜欢这种方法:
-首先构建引擎
-然后使用专用的
发动机(…)
setter来制造汽车

Engine engine = new EngineBuilder().name("Diesel").weight(5).build();
Car car = new CarBuilder().engine(engine).build();

我更喜欢这种方法:
-首先构建引擎
-然后使用专用的
发动机(…)
setter来制造汽车

Engine engine = new EngineBuilder().name("Diesel").weight(5).build();
Car car = new CarBuilder().engine(engine).build();

这最终是一个主观问题

也就是说,作为对比:谷歌代码库中的Java代码使用了很多构建器,特别是对于协议缓冲区之类的数据对象。如果内联表达式满足要求,则通常倾向于避免使用局部变量,这会导致大量代码看起来像您的示例

一个显著的区别是,谷歌风格鼓励一种叫做矩形规则的东西(不幸的是,我找不到它的链接,尽管有提到)。(我想说John DeTreville提出了这个想法,但我不是100%肯定。)从本质上讲,矩形规则说,您希望语句中的每个表达式都适合一个矩形字符块。在表达式的矩形中可以有子表达式(每个子表达式都位于各自的矩形中),但外部表达式的任何部分都不应位于内部表达式的矩形中

(当我在那里的时候,大家一致认为外部表达式的结束括号不算侵入内部矩形,但外部控制语句的结束括号算侵入内部矩形。显然,你可以花一整天的时间来讨论这样的风格问题。)

这意味着,像你所展示的那样,写作的首选方式更像:

Car car =
    Car.builder()
        .engine(
            Engine.builder()
                .name("Diesel")
                .weight(5)
                .build()
        )
        .build();
或者,更常见的情况是(稍微弯曲矩形规则):

我个人认为最后一个例子最容易阅读:它紧凑而不繁忙,并能吸引人们注意陈述的重要部分,而不会因为不重要的细节而分散注意力。文本的总体布局与数据结构相匹配,这也有助于理解


当然,这只是一种风格选择。但事实上,一个非常大且成功的代码库做出了这样的选择,这表明以这种方式编写代码并没有错,如果您喜欢这样做,您应该可以自由地继续进行。

这最终是一个主观问题

也就是说,作为对比:谷歌代码库中的Java代码使用了很多构建器,特别是对于协议缓冲区之类的数据对象。如果内联表达式满足要求,则通常倾向于避免使用局部变量,这会导致大量代码看起来像您的示例

一个显著的区别是,谷歌风格鼓励一种叫做矩形规则的东西(不幸的是,我找不到它的链接,尽管有提到)。(我想说John DeTreville提出了这个想法,但我不是100%肯定。)从本质上讲,矩形规则说,您希望语句中的每个表达式都适合一个矩形字符块。在表达式的矩形中可以有子表达式(每个子表达式都位于各自的矩形中),但外部表达式的任何部分都不应位于内部表达式的矩形中

(当我在那里的时候,大家一致认为外部表达式的结束括号不算侵入内部矩形,但外部控制语句的结束括号算侵入内部矩形。显然,你可以花一整天的时间来讨论这样的风格问题。)

这意味着,像你所展示的那样,写作的首选方式更像:

Car car =
    Car.builder()
        .engine(
            Engine.builder()
                .name("Diesel")
                .weight(5)
                .build()
        )
        .build();
或者,更常见的情况是(稍微弯曲矩形规则):

我个人认为最后一个例子最容易阅读:它紧凑而不繁忙,并能吸引人们注意陈述的重要部分,而不会因为不重要的细节而分散注意力。文本的总体布局与数据结构相匹配,这也有助于理解


当然,这只是一种风格选择。但事实上,一个非常大且成功的代码库已经做出了这个选择,这表明以这种方式编写代码并没有错,如果您喜欢这样做,您应该可以自由地继续。

这种方式很好,因为它意味着您不必在
CarBuilder
中复制有关引擎构建的内容,或者将方法添加到
EngineBuilder
以“回溯”到
CarBuilder
。例如,这种方法意味着您可以重用
EngineBuilder
来构建其他类型的车辆。您能举例说明一下您所说的替代方法是什么吗?摆脱
新的
调用,并将其转换为静态的
builder
方法。因此,您可以执行
Car.builder().engine(engine.builder()…build()).build()
我将使用
Supplier
重载
CarBuilder.engine
方法,从而使其更加灵活。然后可以将
EngineBuilder
逻辑提取到另一个方法,并使用方法引用。遗憾的是,这个问题被搁置了,所以我不能提供一个完整的答案。这种方式很好,因为它意味着你不必这样做