9

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;
}
4

3 に答える 3

6

通常、Builder から構築されたクラスには、Builder に関する専門的な知識はありません。つまり、 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);
}
于 2010-09-13T19:20:25.040 に答える
3

以前にこのアプローチを見たことがありませんが、うまくいくようです。

基本的には、ビルダー パターンの実装が比較的簡単になりますが、実行時のオーバーヘッドがわずかに高くなります (余分なオブジェクト + クローン操作 + コンパイルされる場合とされない場合があるアクセサー関数の間接レベル)。

考慮したい可能性のあるバリエーション: ビルダー オブジェクト自体を不変にすると、それらを防御的に複製する必要がなくなります。これは、特にビルダーを変更するよりも頻繁にオブジェクトをビルドする場合に、全体的に有利になる可能性があります。

于 2010-09-13T19:18:07.660 に答える
3

この実装は、Josh Bloch の『Effective Java 2nd Edition』で詳しく説明されている実装に似ています。

競合の1つのポイントは、あなたのbuild()方法です。単一のビルダーが不変のインスタンスを作成する場合、その作業が完了したことを考慮して、ビルダーを再度使用できるようにするのは公平でしょうか? ここでの注意点は、不変オブジェクトを作成している場合でも、ビルダーの可変性により、いくつかの「驚くべき」バグが発生する可能性があることです。

これを修正するには、ビルド メソッドでインスタンスを作成し、ビルダーがオブジェクトを再度ビルドできないようにして、新しいビルダーが必要になるようにすることをお勧めします。定型文は退屈に思えるかもしれませんが、後で得られる利点は、現在必要な労力を上回ります。その後、実装クラスはBuilderインスタンスをコンストラクター パラメーターとして受け取ることができますが、ビルダーはインスタンスに必要なすべての状態を引き出しさせ、ビルダー インスタンスを解放し、問題のデータへの最終的な参照を保持する必要があります。

于 2010-09-13T20:15:10.593 に答える