我想实现一个具有流畅界面的构建器。
要求
为了使事情变得更加困难,还有两个额外的要求:
我希望返回的对象是不可变的,以便它可以按如下方式使用,并且接口应该在派生接口中可扩展:
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在How to create an immutable builder of an immutable class that contains a set? 中简要评论了这一点? )
构建器接口应该是可扩展的:
interface Builder<T extends Builder<T>> { T setValue(int v); } interfacte BuilderEx<T extends BuilderEx<T>> { T someOtherOpp(); }
(它使用 Eamonn McManus 1描述的自引用泛型)
执行
为了满足第一个要求,AbstractBuilder 的实现类似于如何创建包含集合的不可变类的不可变构建器?:
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:将 a 转换Builder<T>
为ConcreteBuilder
或ConcreteBuilderEx
取决于T
. 问题是如何实现castToConcrete
由于构建器方法创建新实例,因此覆盖方法getThis
不起作用ConcreteBuilder
。使用提供给 的构造函数并存储在字段中的“函子” 3可以转换为。添加以下字段(它使用 Guava 库3到:AbstractBuilder
Builder<T>
T
AbstractBuilder
Function<Builder<T>, T> castToConcrete;
问题
我目前的实施castToConcrete
变得非常丑陋:
- 这是一
if-else
棵树instanceof
- 当
input instanceof Builder
而不是input instanceof BuilderEx
需要在 中定义所有方法的包装类时,将这些方法BuilderEx
转发Builder
到input
并为其他方法执行默认或 nil 操作。 当[由 OP 编辑]不起作用:我们需要每个接口的包装器[/编辑]input instanceof BuilderEx
它将输入转换为ConcreteBuilderEx
即使input
可能不是实际实例,ConcreteBuilderEx
而是匿名子类的实例AbstractBuilderEx
(希望这可行。)
有什么更好/更清洁的方法来做到这一点?