デザインパターン[GoF 23]#7 Builderパターン

こんにちは!ぐちです。

久しぶりのデザインパターンの続編です。笑

イマイチこのデザインパターンのメリットがわかっていなくて、避けてたわけではないんですが、遠退いてしまっていました。

コードだけの列挙になるような気もしていますが、とりあえず今回は下記の記事七つ目をご紹介したいと思います。

Builderパターン

オブジェクトの生成制御のために使われることが多いそうです。ただのコンストラクタでも実現できるんですが、冗長的になるためにBuilderパターンがあるんですね。

コンストラクタ

では、早速コンストラクタで書かれたものを見てみましょう。

public class People {
    private String name;
    private Integer age;

    public People(String name, Integer age) {
         if (name == null || age == null) {
            throw new NullPointerException();
        }
        this.name = name;
        this.age = age;
    }

     // getter, setter...

     public void sayGoodbye() {
          System.out.println("Good bye!!");
    }
}

上記の通り、コンストラクタのパラメータとして属性を受け取り、設定することはできますが、オプションが増えるとパラメータに大量のnullを渡すような冗長的な書き方になります。

public class Main {
     public static void main(String[] args) {
           People people = new People("John", 18);
     }
}

GoF Builder

続いて、GoFのBuilderパターンを用いたコードをご紹介します。(※本当にご紹介するだけになります。。。よくわかっていません。)

public class People {
     private String name;
     private Integer age;

     // getter, setter...

     public void sayGoodbye() {
          System.out.println("Good bye!!");
     }
}

まずはオブジェクトクラスです。コンストラクタでパラメータを受け取る記述を省いた状態で、他は同じですね。

続いて、パラメータの設定を担っているDirectorクラスですね。

public class Director {
     private Builder builder;

    Director(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.name("Tom");
        builder.age(12);
    }
}

続きまして、オブジェクトの生成ロジックを担うBuilderインターフェイスです。

public interface Builder {
    public void name(String name);
    public void age(Integer age);

    People getResult();
}

これを実装したPeopleBuilderクラスです。

public class PeopleBuilder implements Builder {
     private People people;

    public PeopleBuilder() {
        this.people = new People2();
    }

    @Override
    public void name(String name) {
        people.setName(name);
    }

    @Override
    public void age(Integer age) {
        people.setAge(age);
    }

    @Override
    public People getResult() {
        if (people.getName() == null || people.getAge() == null) {
            throw new NullPointerException();
        }
        return this.people;
    }
}

setterやgetterにあたる自身を返却するメソッドを実装します。

最後に呼び出し元です。

public class Main {
     public static void main(String[] args) {
           Builder builder = new PeopleBuilder();
           Director director = new Director(builder);
           director.construct();
           builder.getResult().sayGoodbye();
     }
}

こうすることで

  • 生成されるオブジェクト(People)と生成ロジック(Builder)が分離され、可読性が高まる。
  • Builderの実装クラスを切り替えることで、生成するPeopleの制御が可能。

というメリットがあるそうです。

逆にデメリットとしては、

  • interfaceや実装など、記述量が多くなる。
  • 生成処理が他のBuilderと比べると複雑。

というものがあるそうです。

僕自身あまりメリットがわかっていないので、コードのご紹介みたいになってしまいました。もっと勉強しないとだめですね。

サンプルコード

GitHubにアップしていますのでご自由にお使いください。

では今回はこの辺で。