java构建器模式和大量必需参数
到目前为止,我使用构建器模式的following实现(与here描述的实现相反):
public class Widget {
public static class Builder {
public Builder(String name, double price) { ... }
public Widget build() { ... }
public Builder manufacturer(String value) { ... }
public Builder serialNumber(String value) { ... }
public Builder model(String value) { ... }
}
private Widget(Builder builder) { ... }
}
这在我遇到的大多数情况下都很有效,在这些情况下,我需要使用各种必需/必需和可选参数构建复杂对象。然而,我最近一直在努力理解,当所有参数都是强制性的(或者至少绝大多数是强制性的)时,这种模式是如何带来好处的
解决这一问题的一种方法是对传递到它们自己的类中的参数进行逻辑分组,以减少传递给构建器构造函数的参数数量
例如,而不是:
Widget example = new Widget.Builder(req1, req2, req3,req4,req5,req6,req7,req8)
.addOptional(opt9)
.build();
按如下方式分组:
Object1 group1 = new Object1(req1, req2, req3, req4);
Object2 group2 = new Object2(req5, req6);
Widget example2 = new Widget.Builder(group1, group2, req7, req8)
.addOptional(opt9)
.build();
虽然使用单独的对象简化了很多事情,但如果不熟悉代码,也会使事情变得有点难以理解。我考虑的一件事是将所有参数移动到它们自己的addParam(param)
方法中,然后在build()
方法中对所需的参数执行验证
什么是最佳实践?是否有更好的方法,我还没有考虑过
# 1 楼答案
fluent builder模式仍然是有益的:
更具可读性-它有效地允许命名参数,因此调用不仅仅是一长串未命名参数
它是无序的-这允许您将参数分组到逻辑组中,或者作为单个生成器setter调用的一部分,或者只允许您使用自然顺序来调用生成器setter方法,这些方法最能理解这个特定的实例化
在合适或自然的情况下,我更喜欢杂交。它不必全部在构造函数或中,每个参数都有自己的addParam方法。Builder为您提供了执行一个、另一个、中间或组合的灵活性:
# 2 楼答案
构建器模式的一个优点是,我很少(如果曾经)看到它被提升,它也可以用于有条件地构造对象,例如,只有在所有必需参数都正确或者其他必需资源可用的情况下。在这方面,它们提供了与a static factory method类似的好处
# 3 楼答案
构建器/工厂仍然允许您将接口与实现类型分离(或者允许您插入适配器等),假设
Widget
成为一个接口,并且您有办法注入或隐藏new Widget.Builder
如果您不关心解耦,并且您的实现是一次性的,那么您是对的:构建器模式并不比普通的构造函数有用多少(它仍然使用每个构建器方法的属性样式标记其参数)
如果重复创建参数变化不大的对象,那么它可能仍然有用。您可以传入、缓存在插入多个属性后获得的中间生成器等:
这假设构建器是不可变的:构建器设置器不设置属性并返回
this
,而是返回自身的新副本和更改。正如您在上面指出的,参数对象是绕过重复参数样板的另一种方法。在这里,您甚至不需要构建器模式:只需将参数对象传递给实现构造函数# 4 楼答案
如果有许多必需参数,则可以使用Step Builder。简而言之:为每个强制参数定义一个接口,生成器方法返回下一个强制生成器接口,或者为可选方法返回生成器本身。生成器仍然是实现所有接口的单个类
用法:
像Kotlin和Scala这样的语言在这里更方便,因为它们提供带有默认值的命名参数
# 5 楼答案
我认为这在您有较大的强制值的情况下是合适的,尽管接口的数量会增加,但代码会很干净
这将强制用户设置所有强制值,并强制设置值的顺序。因此,要构造一个人,这将是生成的代码:
签出此引用: Builder Pattern with Twist
# 6 楼答案
该模式简化了不可变类的创建,提高了可读性代码。考虑下面的人类(用传统的构造函数和生成器)。
哪种施工方法更容易理解
当然,这是cohesion的原则,不管对象构造语义如何,都应该采用