这是不可变类和 Builder 模式的有效 Java 实现吗?

发布于 2024-09-19 17:19:02 字数 1067 浏览 6 评论 0原文

Builder 实现 Cloneable 并重写clone(),并且不可变类保留构建器的私有克隆,而不是复制构建器的每个字段。这使得返回新的构建器并创建不可变实例的稍微修改的副本变得很容易。

这样我就可以去

MyImmutable i1 = new MyImmutable.Builder().foo(1).bar(2).build();
MyImmutable i2 = i1.builder().foo(3).build();

Cloneable 接口据说有些损坏,但是这是否违反了良好的 java 编码实践,这个构造有什么问题吗?

final class MyImmutable { 
  public int foo() { return builder.foo; }
  public int bar() { return builder.bar; }
  public Builder builder() { return builder.clone(); }
  public static final class Builder implements Cloneable {
    public Builder foo(int val) { foo = val; return this; }
    public Builder bar(int val) { bar = val; return this; }
    public MyImmutable build() { return new MyImmutable(this.clone()); }
    private int foo = 0;
    private int bar = 0;
    @Override public Builder clone() { try { return (Builder)super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } }
  }
  private MyImmutable(Builder builder) { this.builder = builder; }
  private final Builder builder;
}

The Builder implements Cloneable and overrides clone() and instead of copying every field of the builder, the immutable class keeps a private clone of the builder. This makes it easy to return a new builder and create slightly modified copies of an immutable instance.

This way I can go

MyImmutable i1 = new MyImmutable.Builder().foo(1).bar(2).build();
MyImmutable i2 = i1.builder().foo(3).build();

The Cloneable interface is said to be somewhat broken, but does any of this violate good java coding practice, are there any problems with this construct?

final class MyImmutable { 
  public int foo() { return builder.foo; }
  public int bar() { return builder.bar; }
  public Builder builder() { return builder.clone(); }
  public static final class Builder implements Cloneable {
    public Builder foo(int val) { foo = val; return this; }
    public Builder bar(int val) { bar = val; return this; }
    public MyImmutable build() { return new MyImmutable(this.clone()); }
    private int foo = 0;
    private int bar = 0;
    @Override public Builder clone() { try { return (Builder)super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } }
  }
  private MyImmutable(Builder builder) { this.builder = builder; }
  private final Builder builder;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

客…行舟 2024-09-26 17:19:35

您的实现与 Josh Bloch 的《Effective Java 第二版》中详细介绍的实现类似。

争论的焦点之一是您的 build() 方法。如果单个构建器创建一个不可变实例,考虑到它的工作已经完成,允许再次使用该构建器是否公平?这里需要注意的是,即使您正在创建一个不可变的对象,构建器的可变性也可能会导致一些相当“令人惊讶”的错误。

为了纠正这个问题,可能建议构建方法应该创建实例,然后使构建器无法再次构建对象,从而需要一个新的构建器。尽管样板文件可能看起来很乏味,但以后获得的好处会超过当前所需的努力。然后,实现类可以接收 Builder 实例作为构造函数参数,但构建器随后应该让实例提取所有需要的状态,释放构建器实例并保留对相关数据的最终引用。

Your implementation is similar to the implementation detailed in Josh Bloch's Effective Java 2nd Edition.

One point of contention would be your build() method. If a single builder creates an immutable instance, would it be fair to allow the builder to be used again, considering that it's work has been done? The caution here is that even though you are creating an immutable object, the mutability of your builder may result in a few rather "surprising" bugs.

To correct this, it may be suggested that the build method should create the instance and then make the builder incapable of building the object again, requiring a new builder. Although the boiler-plate may seem tedious, the benefits to be reaped later outweigh the required effort at present. The implementation class can then receive a Builder instance as a constructor parameter, but the builder should then let the instance pull out all it's needed state, releasing the builder instance and preserving final references to the data in question.

蛮可爱 2024-09-26 17:19:27

我以前没有见过这种方法,但看起来效果很好。

基本上,它使构建器模式的实现相对简单,但代价是运行时开销稍高(额外的对象+克隆操作+访问器函数中可能会或可能不会编译出来的间接级别)。

您可能需要考虑的一个潜在变化:如果您使构建器对象本身不可变,则无需防御性地克隆它们。这可能是一个全面的胜利,特别是如果您构建对象的频率比更改构建器的频率高得多。

I've not seen this approach before, but looks like it would work fine.

Basically it makes the builder pattern relatively simple to implement, at the expense of slightly higher runtime overhead (extra objects + cloning operations + a level of indirection in accessor functions that may or may not get compiled out).

A potential variation you might want to consider: if you make the builder objects themselves immutable, you won't need to defensively clone them. This could be an overall win, especially if you build objects a lot more frequently than you change the builders.

别挽留 2024-09-26 17:19:20

通常,从生成器构造的类没有任何生成器的专业知识。也就是说 Immutable 将有一个构造函数来提供 foo 和 bar 的值:

public final class MyImmutable {
  public final int foo;
  public final int bar;
  public MyImmutable(int foo, int bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

该构建器将是一个单独的类:

public class MyImmutableBuilder {
  private int foo;
  private int bar;
  public MyImmutableBuilder foo(int val) { foo = val; return this; }
  public MyImmutableBuilder bar(int val) { bar = val; return this; }
  public MyImmutable build() { return new MyImmutable(foo, bar); }
}

如果您愿意,您可以向 MyImmutable 构建器添加一个静态方法,以从现有的 MyImmutable 实例开始:

public static MyImmutableBuilder basedOn(MyImmutable instance) {
  return new MyImmutableBuilder().foo(instance.foo).bar(instance.bar);
}

Generally the class constructed from the Builder doesn't have any specialized knowledge of the builder. That is Immutable would have a constructor to supply the value for foo and bar:

public final class MyImmutable {
  public final int foo;
  public final int bar;
  public MyImmutable(int foo, int bar) {
    this.foo = foo;
    this.bar = bar;
  }
}

The builder would be a separate class:

public class MyImmutableBuilder {
  private int foo;
  private int bar;
  public MyImmutableBuilder foo(int val) { foo = val; return this; }
  public MyImmutableBuilder bar(int val) { bar = val; return this; }
  public MyImmutable build() { return new MyImmutable(foo, bar); }
}

If you wanted, you could add a static method to MyImmutable builder to start from an existing MyImmutable instance:

public static MyImmutableBuilder basedOn(MyImmutable instance) {
  return new MyImmutableBuilder().foo(instance.foo).bar(instance.bar);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文