【发布时间】:2012-05-16 13:22:32
【问题描述】:
我想实现一个界面流畅的构建器。
要求
为了让事情变得更加困难,还有两个额外的要求:
-
我希望返回的对象是不可变的,以便它可以按如下方式使用,并且接口应该可以在派生接口中扩展:
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 的实现类似于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 或@987654337 @ 取决于 T 。问题是如何实现castToConcrete
由于构建器方法创建新实例,覆盖ConcreteBuilder 中的getThis 的方法不起作用。使用提供给AbstractBuilder 的构造函数并存储在字段中的“函子”3 可以将Builder<T> 转换为T。添加以下字段(它使用 Guava 库3 到 AbstractBuilder:
Function<Builder<T>, T> castToConcrete;
问题
我当前对castToConcrete 的实现变得非常丑陋:
- 这是
if-elseinstanceof上的树 - 当
input instanceof Builder而不是input instanceof BuilderEx的包装类需要在BuilderEx中定义所有方法时,将Builder中的方法转发到input并对其他人执行默认或nil 操作。李> -
当[由 OP 编辑]不起作用:我们需要每个接口的包装器[/edit]input instanceof BuilderEx时,它会将输入转换为ConcreteBuilderEx,即使input可能不是ConcreteBuilderEx的实际实例,而是AbstractBuilderEx的匿名子类的实例(希望这可行。)
有什么更好/更清洁的方法来做到这一点?
【问题讨论】:
标签: java design-patterns generics builder fluent-interface