我想实现一个具有流畅界面的构建器。
为了使事情变得更加困难,还有两个额外的要求:
我希望返回的对象是不可变的,以便可以按如下方式使用,接口应该在派生接口中可扩展:
ConcreteBuilder b1 = builder().setValue(1);
ConcreteBuilder b2 = b1.setValue(2);
ComplexObject o1 = b1.build();
o1.getValue(); // should return 1
ComplexObject o2 = b2.build();
o2.getValue(); // should return 2
首先讨论一个问题:构建器是否应该是不可变的,以便它具有以上语义? (Jonathan)
构建器界面应该是可扩展的:
interface Builder<T extends Builder<T>> {
T setValue(int v);
}
interfacte BuilderEx<T extends BuilderEx<T>> {
T someOtherOpp();
}
(它使用Eamonn McManus所描述的自引用泛型 1 )
为了满足第一个要求,AbstractBuilder的实现类似于How to create an immutable builder of an immutable class that contains a set?中的实现:
class AbstractBuilder<T extends Builder<T>> implements Builder<T> {
T setValue(final int v) {
return castToConcrete(new AbstractBuilder<T>() {
// with build() overridden to use v
}
}
}
castToConcrete
应与self()
1 和getThis()
2 类似:转换为Builder<T>
至ConcreteBuilder
或ConcreteBuilderEx
取决于T
。问题是如何实施castToConcrete
由于构建器方法创建新实例,因此覆盖getThis
中ConcreteBuilder
的方法不起作用。使用提供给AbstractBuilder
的构造函数并存储在字段中的“functor” 3 可以将Builder<T>
转换为T
。添加以下字段(它使用Guava库 3 到AbstractBuilder
:
Function<Builder<T>, T> castToConcrete;
我目前对castToConcrete
的实施变得相当丑陋:
if-else
instanceof
树
input instanceof Builder
而不是input instanceof BuilderEx
需要在BuilderEx
中定义所有方法的包装类时,将Builder
中的方法转发到input
并执行其他人的默认或无操作。input instanceof BuilderEx
将输入转换为ConcreteBuilderEx
时,即使input
可能不是ConcreteBuilderEx
的实际实例,而是AbstractBuilderEx
的匿名子类的实例1}}(希望这有效。)有什么更好/更清洁的方法呢?
答案 0 :(得分:3)
干净的方法是不使用AbstractBuilder
,而是为您需要构建的每种类型创建一个特定的构建器。通过使构建器方法可读并根据您要构建的对象进行自定义,您可以获得流畅的界面。如果您需要创建大量构建器,则可以使用代码生成,但这些方法的设计不会很好。