Joshua BlochのBuilderデザインパターンの改善?


12

2007年に、Joshua Blochsが「ビルダーパターン」を採用し、コンストラクターとセッターの過剰使用を改善するためにどのように変更できるかについての記事を読みました。このデザインパターンの簡単な概要は、こちらで説明されています

私はこのアイデアが好きで、それ以来ずっと使っています。それに伴う問題は、クライアントの観点からは非常にクリーンで使いやすいものですが、それを実装するのは苦痛になる可能性があります!オブジェクトには単一のプロパティが参照される非常に多くの異なる場所があるため、オブジェクトを作成し、新しいプロパティを追加するには時間がかかります。

だから...私は考えていました。まず、Joshua Blochスタイルのオブジェクトの例:

ジョシュブロッホスタイル:

public class OptionsJoshBlochStyle {

    private final String option1;
    private final int option2;
    // ...other options here  <<<<

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private String option1;
        private int option2;
        // other options here <<<<<

        public Builder option1(String option1) {
            this.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.option2 = option2;
            return this;
        }

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

    private OptionsJoshBlochStyle(Builder builder) {
        this.option1 = builder.option1;
        this.option2 = builder.option2;
        // other options here <<<<<<
    }

    public static void main(String[] args) {
        OptionsJoshBlochStyle optionsVariation1 = new OptionsJoshBlochStyle.Builder().option1("firefox").option2(1).build();
        OptionsJoshBlochStyle optionsVariation2 = new OptionsJoshBlochStyle.Builder().option1("chrome").option2(2).build();
    }
}

今、私の「改善された」バージョン:

public class Options {

    // note that these are not final
    private String option1;
    private int option2;
    // ...other options here

    public String getOption1() {
        return option1;
    }

    public int getOption2() {
        return option2;
    }

    public static class Builder {

        private final Options options = new Options();

        public Builder option1(String option1) {
            this.options.option1 = option1;
            return this;
        }

        public Builder option2(int option2) {
            this.options.option2 = option2;
            return this;
        }

        public Options build() {
            return options;
        }
    }

    private Options() {
    }

    public static void main(String[] args) {
        Options optionsVariation1 = new Options.Builder().option1("firefox").option2(1).build();
        Options optionsVariation2 = new Options.Builder().option1("chrome").option2(2).build();

    }
}

「改良版」でわかるように、追加のプロパティ(またはこの場合はオプション)に関するコードを追加する必要がある場所が2つ少なくなっています!私が見ることができる唯一のマイナスは、外側のクラスのインスタンス変数がfinalになれないことです。しかし、クラスはこれがなければ不変です。

この保守性の改善には、本当にマイナス面はありますか?私が見ないネストされたクラス内でプロパティを繰り返した理由がなければなりませんか?


これは、ここでのC#でのビルダーパターンの採用と非常によく似ています
-MattDavey

回答:


12

あなたのバリエーションはとてもいいです。ただし、ユーザーはこれを行うことができます。

Options.Builder builder = new Options.Builder().option1("firefox").option2(1);
Options optionsVariation1 = builder.build();
assert optionsVariation1.getOption1().equals("firefox");
builder.option1("chrome");
assert optionsVariation1.getOption1().equals("firefox"); // FAILURE!

むしろオブジェクトを無効にします。

buildこれを行うためにメソッドを変更できます:

public Options build() {
    Options options = this.options;
    this.options = null;
    return options;
}

これはこれを防ぎます-呼び出し後のビルダーのセッターメソッドへのbuild呼び出しは、NullPointerExceptionで失敗します。フラッシュしたい場合は、nullをテストし、代わりにIllegalStateExceptionまたは何かをスローすることもできます。そして、それをすべてのビルダーで使用できる汎用ベースクラスに移動できます。


1
2行目を次のように変更build()しますthis.options = new Options();。このように、Optionsインスタンスは安全に不変であり、ビルダーは同時に再利用可能です。
ナティックス

5

Blochのパターンのビルダーを何度も使用して、「ほぼ」同じオブジェクトを生成できます。さらに、不変オブジェクト(すべてのフィールドはfinalであり、それ自体は不変です)には、変更が無効になる可能性があるスレッドセーフの利点があります。


0

Optionsが効果的に複製可能である場合(つまり、Cloneableインターフェイスに関係なく)、プロトタイプパターンを使用できます-ビルダーに1つを作成し、build()で複製します。

Cloneableインターフェイスを使用しない場合は、すべてのフィールドをコピーする必要があるため、追加する必要がある場所を追加します。少なくとも、Cloneableを実際に使用する単純なフィールドを持つクラスの場合は良い考えです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.