到目前为止,我使用构建器模式的following实现(与这里描述的实现相反):
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()
方法中对所需参数执行验证。
什么是最佳实践?是否有我尚未考虑过的更好的方法?
8条答案
按热度按时间qybjjes11#
如果您有许多必需的参数,则可以使用Step Builder。简而言之:你为每一个强制参数定义一个接口,一个生成器方法返回下一个强制生成器接口,或者为可选方法返回生成器本身。2生成器仍然是一个实现所有接口的类。
用法:
请记住,我将类命名为
StepBuilder
只是为了在我的解释中使它更清楚,最好给它一个反映相应域的某些方面的名称。像Kotlin和Scala这样的语言在这里更方便,因为它们提供了带有默认值的命名参数。
rnmwe5a22#
然而,我最近一直在努力理解,当所有参数都是强制性的(或者至少绝大多数参数都是强制性的)时,这种模式有什么好处。
fluent builder模式仍然是有益的:
1.它的可读性更强--它有效地允许命名参数,这样调用就不仅仅是一长串未命名参数
1.它是无序的--这让你可以将参数分组到逻辑组中,或者作为单个构建器setter调用的一部分,或者简单地让你使用自然的顺序来调用构建器setter方法,使这个特定的示例化最有意义。
分组如下:
虽然使用单独的对象可以大大简化工作,但如果不熟悉代码,也会使事情变得有点难以理解。我考虑的一件事是将所有参数移到它们自己的
addParam(param)
方法中,然后在build()
方法中对所需参数执行验证。如果合适或自然的话,我会喜欢混合的。它不一定都在构造函数中 * 或 * 每个param都有自己的addParam方法。Builder给你灵活性来做一个,另一个,中间,或组合:
rvpgvaaj3#
最近我一直在努力理解,当所有参数都是强制性的时,模式有什么好处
这种模式简化了不可变类的创建,提高了代码的可读性。考虑下面的Person类(带有一个传统的构造函数和一个构建器)。
哪种构造方法更容易理解?
解决这个问题的一种方法是对传递给它们自己的类的参数进行逻辑分组
当然,这是cohesion的原则,无论对象构造语义如何,都应该采用。
zbdgwd5y4#
构建器模式的一个优点是我很少看到它被推广,它也可以用于有条件地构造对象,例如只有当所有强制参数都正确或者其他所需资源可用时,在这方面,它们提供了与static factory method类似的好处。
ogsagwnx5#
我认为这将是适当的情况下,你有大的强制值,虽然接口的数量会增加,但代码会干净
这将强制用户设置所有强制值,并强制设置值的顺序。因此,要构造一个人,结果代码如下:
查看此参考资料:Builder Pattern with Twist
n6lpvg4x6#
构建器/工厂仍然允许您将接口与实现类型解耦(或者允许您插入适配器等),假设
Widget
成为一个接口,并且您有办法注入或隐藏new Widget.Builder
。如果您不关心解耦,并且您的实现是一次性的,那么您是对的:builder模式并不比普通构造函数有用多少(它仍然用attribute-per-builder-method样式标记其参数)。
如果你重复地创建参数变化不大的对象,那么它可能仍然是有用的。你可以传递、缓存等插入几个属性后获得的中间构建器:
这假设您的构建器是不可变的:builder setter不会设置一个属性并返回
this
,而是返回自身的一个新副本,其中包含修改。正如您在上面指出的,parameter对象是另一种避开重复参数样板的方法。在那里,您甚至不需要builder模式:只需将参数对象传递给实现构造函数。zysjyyx47#
我的匿名类解决方案。这里是
familyName
是必需参数,givenName
是可选的。如果这个解决方案的主要目标是强制创建Person
来设置必需参数的程序员(如果他不这样做,Java将无法编译)。实际上,目标并未完全实现:因为我不能强迫程序员写
this.familyName = familyName;
,但是他必须实现setFamilyName
,如果程序员不是低能的,他知道他必须在这个方法中做什么,但是他可能因为疲劳而忘记。实施:
t8e9dugd8#
我刚刚发布了一个免费的Intellij插件来解决这类问题。本质上,你可以为构建器中的每个参数定义一个方法,你可以注解哪些是强制的,哪些不是,IntelliJ完成会突出显示哪些是强制的和可选的参数。请随意尝试一下:
https://github.com/banterly91/Java-Builder-Guided-Completion-Intellij-Plugin