また、カプセル化されたオブジェクトを構築するためにBuilderを使用し

タイトル;

私は別のクラスをカプセル化するクラスを持っている想像してみて:

@Builder
public class Dragon {

  private Dimensions dimensions;
  private String name;

  public static class ParentBuilder {
    DimensionsBuilder innerBuilder = Dimensions.builder();

    public DragonBuilder height(double height) {
      this.innerBuilder.height(height);
      return this;
    }

    public DragonBuilder length(double length) {
      this.innerBuilder.length(length);
      return this;
    }

    public Dragon build() {
      return Dragon.builder()
        .dimensions(this.innerBuilder.build())
        .name(this.name)
        .build();
    }
  } 
}

@Builder
public class Dimensions {
  private double height;
  private double length;
}

これは、(残念ながら、ないドラゴンについて、である)実際のコードは、デリゲート非常に簡単な例であることに留意してください多くのプロパティのはinnerBuilder

このように、私はこのようなクラスをインスタンス化することができます:

Dragon dragon = Dragon.builder()
  .height(12.0)
  .length(25.0)
  .name("Smaug")
  .build();

代わりに、このように:

Dragon dragon = Dragon.builder()
  .dimensions(Dimensions.builder()
    .height(12.0)
    .length(25.0)
    .build())
  .name("Smaug")
  .build;

直接、あまりにも内部クラスを構築するビルダーメソッドを追加するのは良いコーディングプラクティスですか?多分それはあまりにも密接に結合されているためか、それは、いくつかの設計原理を怒りませんか?

私はすでに遭遇する1つの問題は、内部クラスのリファクタリングをしていたとき、私はまた、親クラスにほとんど同じリファクタリングを適用しなければなりませんでした。

ヤンリーケ:

私の意見では、スタイル/設計の観点から、あなたのアプローチとは根本的に間違っているものは何もありません。しかし、コメントでユーザーJB Nizetで説明したように、2つの大きな問題があります。

  1. あなたはすべての外国ビルダーメソッドを複製する必要があるので、あなたは、メンテナンスの問題に実行します。(ロンボク島の@Delegateそれはロンボク自体によって生成されたクラスでは動作しないので缶が、ここであなたを助けていません。)
  2. あなたのビルダーのユーザーは両方呼び出すことができdimensions(Dimensions)、非常に混乱している委任メソッドを、。

ユーザーの観点から、私はこのように使用するビルダーをしたいと思います。

Dragon dragon = Dragon.builder()
    .dimensions()
        .height(12.0)
        .length(25.0)
        .back()
    .name("Smaug")
    .build();

これは、あなたが(ロンボク島1.18.8を使用して)それを達成することができる方法です。

@Builder
public class Dragon {
    private Dimensions dimensions;
    private String name;

    public static class DragonBuilder {

        private Dimensions.DimensionsBuilder innerBuilder = 
                new Dimensions.DimensionsBuilder(this);

        // If a method of the same name exists, Lombok does not generate
        // another one even if the parameters differ.
        // In this way, users cannot set their own dimensions object.
        public Dimensions.DimensionsBuilder dimensions() {
            return innerBuilder;
        }

        // Customize build() so that your innerBuilder is used to create 
        // the Dimensions instance.
        public Dragon build() {
            return new Dragon(innerBuilder.build(), name);
        }
    }
}

以下のためのビルダーは、Dimensionsコンテナへの参照を保持していますDragonBuilder

// Don't let Lombok create a builder() method, so users cannot 
// instantiate builders on their own.
@Builder(builderMethodName = "")
public class Dimensions {
    private double height;
    private double length;

    public static class DimensionsBuilder {
        private Dragon.DragonBuilder parentBuilder;

        // The only constructor takes a reference to the containing builder.
        DimensionsBuilder(Dragon.DragonBuilder parentBuilder) {
            this.parentBuilder = parentBuilder;
        }

        // Provide a method that returns the containing builder.
        public Dragon.DragonBuilder back() {
            return parentBuilder;
        }

        // The build() method should not be called directly, so 
        // we make it package-private.
        Dimensions build() {
            return new Dimensions(height, length);
        }
    }
}

このアプローチは、スケールロンボクが自動的ビルダーで必要なすべての残りのセッターメソッドが生成されるため。さらに、自分の提供するユーザーのために何の驚きがないことがあるDimensionsインスタンスを。(あなたはその可能性がありますが、私は強く両方のメソッドが呼び出されたかどうかをチェックすることにより、例えば、その後、潜在的な競合のランタイムチェックを行うことをお勧めします。)

欠点は、それがあるDimensions.builder()、それは直接、または持っている他のクラスのビルダーで使用することはできませんので、これ以上使用できないDimensionsフィールドを。用途:しかし、そこにもそのためのソリューションです@SuperBuilder Dimensionsし、定義class NestedDimensionsBuilder extends Dimensions.DimensionsBuilder<Dimensions, NestedDimensionsBuilder>の中にDragonBuilder

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=228811&siteId=1