【问题标题】:How to implement builder pattern with fluent-interface with immutable results and extended interfaces?如何使用具有不可变结果和扩展接口的 fluent-interface 实现构建器模式?
【发布时间】:2012-05-16 13:22:32
【问题描述】:

我想实现一个界面流畅的构建器。

要求

为了让事情变得更加困难,还有两个额外的要求:

  1. 我希望返回的对象是不可变的,以便它可以按如下方式使用,并且接口应该可以在派生接口中扩展:

    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
    

    首先要讨论一个问题:构建器是否应该是不可变的,以便它具有上述语义? (JonathanHow to create an immutable builder of an immutable class that contains a set?中简要评论过)

  2. 构建器接口应该是可扩展的:

    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()1getThis()2:将Builder&lt;T&gt; 转换为ConcreteBuilder 或@987654337 @ 取决于 T 。问题是如何实现castToConcrete

由于构建器方法创建新实例,覆盖ConcreteBuilder 中的getThis 的方法不起作用。使用提供给AbstractBuilder 的构造函数并存储在字段中的“函子”3 可以将Builder&lt;T&gt; 转换为T。添加以下字段(它使用 Guava 库3AbstractBuilder:

Function<Builder<T>, T> castToConcrete;

问题

我当前对castToConcrete 的实现变得非常丑陋:

  1. 这是if-elseinstanceof 上的树
  2. input instanceof Builder 而不是input instanceof BuilderEx 的包装类需要在BuilderEx 中定义所有方法时,将Builder 中的方法转发到input 并对其他人执行默认或nil 操作。李>
  3. input instanceof BuilderEx 时,它会将输入转换为ConcreteBuilderEx,即使input 可能不是ConcreteBuilderEx 的实际实例,而是AbstractBuilderEx 的匿名子类的实例(希望这可行。)[由 OP 编辑​​]不起作用:我们需要每个接口的包装器[/edit]

有什么更好/更清洁的方法来做到这一点?

【问题讨论】:

    标签: java design-patterns generics builder fluent-interface


    【解决方案1】:

    干净的方法是不使用AbstractBuilder,而是为您需要构建的每种类型创建一个特定的构建器。通过使构建器方法可读并针对您尝试构建的对象进行定制,您可以获得流畅的界面。如果您需要创建大量构建器,您可以使用代码生成,但方法设计得不太好。

    【讨论】:

      猜你喜欢
      • 2010-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      • 2011-05-24
      • 1970-01-01
      • 1970-01-01
      • 2013-07-26
      相关资源
      最近更新 更多