Java 中是否有标准做法,在使用构建器模式时,确保成员变量最多设置一次。我需要确保 setter 被调用 0 或 1 次,但绝不会更多。我想扔一个RuntimeException
某种类型的问题,但我担心同步问题以及该领域的最佳实践。
什么也没有wrong如果用户以您所描述的非法方式调用方法,则会引发异常,但它并不是非常优雅。构建器模式背后的想法是让用户编写流畅、可读的对象定义,而编译时安全性是其中的重要组成部分。如果用户不能确信构建器即使编译也会成功,那么您就引入了用户现在需要理解和解释的额外复杂性。
有几种方法可以完成您所描述的任务,让我们来探讨一下:
-
只让用户做他们想做的事
构建器的一大好处是它们可以让您从同一个构建器构建多个不同的对象:
List<Person> jonesFamily = new ArrayList<>();
Person.Builder builder = new Person.Builder().setLastName("Jones");
for(String firstName : jonesFamilyFirstNames) {
family.add(builder.setFirstName(firstName).build());
}
我认为你有充分的理由禁止这种行为,但如果我没有指出这个有用的技巧,那就是我的失职。也许您一开始就不需要限制这一点。
-
提出异常
你建议提出一个例外,这肯定会起作用。就像我说的,我不认为这是最优雅的解决方案,但这是一种实现(使用Guava https://code.google.com/p/guava-libraries/'s 前提条件 https://code.google.com/p/guava-libraries/wiki/PreconditionsExplained,为了增加可读性):
public class Builder {
private Object optionalObj = null;
// ...
public Builder setObject(Object setOnce) {
checkState(optionalObj == null, "Don't call setObject() more than once");
optionalObj = setOnce;
}
// ...
}
这引发了一个IllegalStateException http://docs.oracle.com/javase/7/docs/api/java/lang/IllegalStateException.html,所以你可以直接打电话throw new IllegalStateException()
如果你不使用番石榴(你应该......:))。假设您没有在线程之间传递构建器对象,则应该不会出现同步问题。如果是,您应该进一步思考为什么在不同的线程中需要相同的构建器 - 这几乎肯定是一种反模式。
-
根本不提供方法
这是防止用户调用您不希望他们调用的方法的最干净、最清晰的方法 - 首先不要提供它。相反,重写构建器的构造函数或build()
方法,以便他们可以选择在当时传递值,但不能在其他时间传递值。通过这种方式,您可以清楚地保证每个构造的对象最多可以设置一次该值。
public class Builder {
// ...
public Obj build() { ... }
public Obj build(Object onceOnly) { ... }
}
-
使用不同的类型来公开某些方法
我实际上并没有这样做,它可能比它的价值更令人困惑(特别是,您可能需要使用自约束泛型 http://www.artima.com/weblogs/viewpost.jsp?thread=136394对于中的方法Builder
),但它是在我写作时想到的,并且对于某些用例来说可能非常明确。将受限制的方法放在构建器的子类中,并且该方法返回父类型,从而防止调用者重新调用该方法。如果这没有意义,一个例子可能会有所帮助:
public class Builder {
// contains regular builder methods
}
public class UnsetBuilder extends Builder {
public Builder setValue(Object obj) { ... }
}
// the builder constructor actually returns an UnsetBuilder
public static UnsetBuilder builder() { ... }
然后我们可以这样调用:
builder().setValue("A").build();
但是如果我们尝试调用,我们会得到一个编译时错误:
builder().setValue("A").setValue("B").build();
because setValue()
回报Builder
,其中缺少一个setValue()
方法,从而防止第二种情况。要完全正确这将是很棘手的(如果用户将Builder
回到一个UnsetBuilder
?)但经过一些努力就会做你正在寻找的事情。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)