web-dev-qa-db-ja.com

ビルダーパターンと構成オブジェクト

ビルダーパターンは不変オブジェクトを作成するのに人気がありますが、ビルダーを作成するにはプログラミングのオーバーヘッドがあります。だから、なぜ単純に構成オブジェクトを使用しないのだろうか。

ビルダーの使用法は次のようになります。

Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();

これが非常に読みやすく簡潔であることは明らかですが、ビルダーを実装する必要があります。

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    private Product(Builder builder) {
        this.name = builder.name;
        this.alcohol = builder.alcohol;
        this.size = builder.size;
        this.price = builder.price;
    }

    public static class Builder {

        private String name;
        private float alcohol;
        private float size;
        private float price;

        // mandatory
        public static Builder name(String name) {
            Builder b = new Builder();
            b.name = name;
            return b;
        }

        public Builder alcohol(float alcohol) {
            this.alcohol = alcohol;
            return.this;
        }

        public Builder size(float size) {
            this.size = size;
            return.this;
        }

        public Builder price(float price) {
            this.price = price;
            return.this;
        }

        public Product build() {
            return new Product(this);
        }

    }

}

私の考えは、次のような単純な構成オブジェクトを使用してコードを削減することです。

class ProductConfig {

        public String name;
        public float alcohol;
        public float size;
        public float price;

        // name is still mandatory
        public ProductConfig(String name) {
            this.name = name;
        }

}

public class Product {

    public final String name;
    public final float alcohol;
    public final float size;
    public final float price;

    public Product(ProductConfig config) {
        this.name = config.name;
        this.alcohol = config.alcohol;
        this.size = config.size;
        this.price = config.price;
    }

}

使用法:

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

この使用法にはさらに数行が必要ですが、非常に読みやすくなっていますが、実装ははるかに単純であり、ビルダーパターンに精通していない人にとっては理解しやすいかもしれません。ちなみに、このパターンの名前はありますか?

私が見落としていた設定アプローチに欠点はありますか?

29
deamon

ビルダーパターンはデカップリングを改善します-製品はインターフェースであり、実装(または場合によっては実装)について知っている唯一のクラスはビルダーです。ビルダーがインターフェースも実装している場合は、これをコードに挿入して、デカップリングをさらに増やすことができます。

このデカップリングは、コードがより保守しやすく、テストが容易であることを意味します。

16

すでに指摘されているように、ビルダーパターンのいくつかの利点が失われています(newは醜く、メンテナンスが難しく、クリーンなビルダーと比較して詳細がリークされます)。

しかし、私が最も見逃しているのは、ビルダーパターンを使用して、いわゆる"fluent interfaces"を提供できることです。

これの代わりに:

ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);

できるよ:

ProductFactory.create()
    .drink("Vodka")
    .whereAlcohoolLevelIs(0.38)
    .inABottleSized(0.7)
    .pricedAt(17.99)
    .build();

誰もが流暢なインターフェースを好むわけではありませんが、それらは間違いなくビルダーパターンの非常に優れた使用法です(すべての流暢なインターフェースはビルダーパターンを使用する必要がありますが、すべてのビルダーパターンが流暢なインターフェースであるとは限りません)。

いくつかの素晴らしいJavaコレクションは、Googleコレクションのように、"流暢なインターフェイス"を非常にリベラルで非常にうまく利用しています。 "入力しやすい/文字が少ない"アプローチ:)

6
NoozNooz42

パターンでどのような問題を解決しようとしていますか?ビルダーパターンは、多数の(オプションの)パラメーターを持つオブジェクトに使用され、大量の異なるコンストラクターや非常に長いコンストラクターを防ぎます。また、構築中にオブジェクトを一貫した状態(対javabeanパターン)に保ちます。

ビルダーと「構成オブジェクト」(良い名前のように感じます)の違いは、コンストラクターまたはゲッター/セッターによって同じパラメーターでオブジェクトを作成する必要があることです。これは、a)コンストラクターの問題を解決しないか、b)構成オブジェクトを一貫性のない状態に保ちます。構成オブジェクトの一貫性のない状態は実際にはそれを傷つけませんが、未完成の構成オブジェクトをパラメーターとして渡すことができます。 [ファントムタイプへのミシッドリンクはこの問題を解決するようですが、それでも読みやすさが失われます(new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>ちょっとひどいです。]これがビルダーパターンの大きな利点です。オブジェクトを作成する前にパラメーターを検証できますそして任意のサブタイプを返すことができます(動作工場のように)。

構成オブジェクトは、すべて必須のパラメーターのセットに対して有効です。このパターンは、以前に.NETまたはJavaで何度も見たことがあります。

4
atamanroman

builder-builder の使用を検討しましたか?

ビルダー(「With」のような接頭辞が付いている)は、より自然に/流暢に読むと思います。

IMO、検証などがある場合、ビルダーパターンははるかに堅牢です。

あなたの場合のビルダーパターンは、次のように変更できます。

_Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
_

build()メソッドは、ビルダーに必要なすべての検証作業を実行できます。ビルダーパターンには、いくつかの設計上の利点もあります(これらすべてがあなたのケースに適用できない場合があります)。詳細については、これを確認してください 質問

1
naikus

個人的には、ビルダーパターンは、これらのオブジェクトが実際に使用されている場所で、よりクリーンなコードを提供していると感じています。一方、ゲッター/セッターがないことは、キャメルケースのゲッター/セッターを期待する多くのフレームワークではあまり使用できません。これは私が感じる深刻な欠点です。

ゲッター/セッターで私が気に入っているのは、自分が何をしているかがはっきりとわかることです。つまり、getまたはsetです。ビルダーを使用すると、ここで直感的な明快さが少し失われているように感じます。

多くの人が特定の本を読んだこと、そして今や突然、ビルダーパターンが新しいiPhoneであるかのように誇大宣伝を楽しんだことを私は知っています。ただし、私はアーリーアダプターではありません。 「新しい方法」を使用するのは、パフォーマンス、メンテナンス、コーディングなど、どの領域でも大幅な時間の節約になる場合のみです。

私の実際の経験では、通常、ゲッター/セッターとコンストラクターの方が優れています。これにより、これらのPOJOをあらゆる目的で再利用できます。

Configオブジェクトの目的はわかりますが、ビルダーよりもさらにオーバーヘッドがあると思います。何のためにあるのでしょうか。セッターの何が問題になっていますか?

たぶん、WITH句を発明する必要があります:例、あなたが持っているとしましょう

public Class FooBar() {
    private String foo;

    public void setFoo(String bar) { 
      this.foo = bar; 
    }

    public String getFoo() { 
        return this.foo; 
    }
}

public static void main(String []args) {

 FooBar fuBar = new FooBar();
 String myBar;

 with fuBar {
    setFoo("bar");
    myBar = getFoo(); 
 }
}

ああ、わかりません...これにより、内部クラスの煩わしさがなくても、コードの記述が速くなる可能性があると思います。 Oracle Javaの第一人者と関係のある人はいますか?

ビルダーでオブジェクトを使用するほどきれいには見えませんが、ビルダーの構築時間を節約できます。また、フレームワークで使用できる通常のpojo/beanとしてクラスを使用することもできます...

あなたたちは実際にこの条項が好きですか、それともそれはむしろ悪いと思いますか?乾杯

1
Lawrence

構成パターンとビルダーパターンは機能的に同等です。それらは両方とも同じ問題を解決します-

  • 複数のコンストラクター署名の必要性を排除します

  • 建設中にのみフィールドを設定できるようにする

  • 消費者が気になる値のみを設定し、他の値には論理的なデフォルトを設定できるようにする

検証を行うメソッドでのみ状態を設定できるようにしたり、カプセル化されたロジックで状態を設定したりするなど、これらのパターンの1つで実行したいことはすべて他のパターンでも実行できます。唯一の本当の違いは、newキーワードを使用してオブジェクトを作成するのが好きな場合、または.build()メソッドを呼び出すのが好きな場合です。

1
TimCrowe

主な欠点は、それがJoshuaの本にないため、ドローンが頭を包むことができないことです。

単純な値オブジェクトを使用して、関数(/ method /コンストラクター)が必要とする複数の引数を保持しています。これには何の問題もありません。これは長年にわたって行われてきました。オプションのパラメータに名前を付けていない限り、このような回避策を考案する必要があります。これは残念なことであり、太陽の神々からの素晴らしい発明ではありません。

本当の違いは、フィールドを直接公開することです。 Joshuaは、パブリックな可変フィールドを持つことは決してありません-しかし彼は、ほとんどがモロンである何百万もの人々によって使用されるAPIを作成し、APIは今後数十年にわたって安全に進化する必要があり、多くの人を割り当てることができます単純なクラスを設計するためだけに数か月

それをエミュレートするのは誰ですか?

0
irreputable