【java】実装ミスをなくす!Builderパターンをjavaで実装する

  • LINEで送る
builderパターン

 本記事ではGoFのデザインパターンのオブジェクトの生成に関するパターンの一つである「Builder」パターンを解説します。このパターンを一言で説明するならば、「オブジェクトの生成過程を抽象化し、動的なオブジェクト生成を可能とするパターン」と言えるでしょう。文章では想像がつきにくいと思いますので、例を踏まえながら解説していきます。

Builderパターンとは

 上述した通り、Builderパターンとはある特定のクラスをインスタンス化するときに使用する技術で、オブジェクトの生成過程を抽象化し、動的なオブジェクト生成が可能となります。

 簡単な言葉で言うと、複数のコンストラクタを設定しなくても、様々なフィールドを持ったオブジェクトを柔軟に生成でき、引数の挿入順序を自由に設定できるということです。

コンストラクタでフィールドに値を指定する方法

 まずはコンストラクタでインスタンスを作成します。ここではhomeクラスを作成し、家の作成に必要なフィールドを持たせます。コンストラクタでインスタンスを生成できるように全てのフィールドをコンストラクタの引数に設定しています。

public class Home {
  private final String basicMaterial;       // 基礎素材
  private final String outerWallMaterial;   // 外壁素材
  private final String outerWallColor;      // 外壁色
  private final String option;              // 追加オプション
  private final int NumberOfFloors;         // 階数

  // コンストラクタ
  public Home(String basicMaterial, String outerWallMaterial, String outerWallColor, String option, int numberOfFloors) {
    this.basicMaterial = basicMaterial;
    this.outerWallMaterial = outerWallMaterial;
    this.outerWallColor = outerWallColor;
    this.option = option;
    NumberOfFloors = numberOfFloors;
  }
}

 実際にインスタンスを作成すると、

Home home = new Home("コンクリート", "レンガ", "赤", "ロフト付き", 2);

 このような生成方法になります。ここでもし作成する家にオプションが無かったり、外壁素材に木材を使うため外壁色を設定しなくて良かったり、プレハブ小屋みたいなもので基礎素材がない場合、コンストラクタで作成しようとしたら引数の数は決まっているので、nullを設定するかコントラクタを増やさないといけません。

  :
  :
  // 基礎素材が必要ない場合
  public home(String outerWallMaterial, String outerWallColor, String option, int numberOfFloors) {
    this.outerWallMaterial = outerWallMaterial;
    this.outerWallColor = outerWallColor;
    this.option = option;
    NumberOfFloors = numberOfFloors;
  }
  
  // カラーとオプションが必要ない場合
  public home(String basicMaterial, String outerWallMaterial, int numberOfFloors) {
    this.basicMaterial = basicMaterial;
    this.outerWallMaterial = outerWallMaterial;
    NumberOfFloors = numberOfFloors;
  }
  :
  :

 このようにコンストラクタを増やせば解決できますが、初期化されていないフィールドができたり、コンストラクタがものすごい数になったりします。

 また、コンストラクタを使用して値をセットする場合に、もし間違った順序で引数を入れてしまったらどうなるでしょう。そこを間違えただけでも、予期せぬインスタンスが生成されていしまいます。

Home home = new Home("コンクリート", "赤", "レンガ", "ロフト付き", 2);

 上記の場合では外壁素材に”赤”、外壁色に”レンガ”が入ってしまいます。コンストラクタでオブジェクトを生成する場合このような不具合に陥る可能性を孕んでいます。

Builderパターンを使用してフィールドに値をセットする方法

Builderパターンを使用したクラスのコードは以下になります。

package com.example.samplegenerateauth;

public class Home {
  private final String basicMaterial;       // 基礎素材
  private final String outerWallMaterial;   // 外壁素材
  private final String outerWallColor;      // 外壁色
  private final String option;              // 追加オプション
  private final int numberOfFloors;         // 階数
  // homeクラスのコンストラクタ
  Home(String basicMaterial, String outerWallMaterial, String outerWallColor,
      String option, int numberOfFloors) {
    this.basicMaterial = basicMaterial;
    this.outerWallMaterial = outerWallMaterial;
    this.outerWallColor = outerWallColor;
    this.option = option;
    this.numberOfFloors = numberOfFloors;
  }
  // builderにも同じフィールドを持っている
  public static class HomeBuilder {
    private String basicMaterial;
    private String outerWallMaterial;
    private String outerWallColor;
    private String option;
    private int numberOfFloors;

    HomeBuilder() {
    }

    public HomeBuilder basicMaterial(final String basicMaterial){
      this.basicMaterial = basicMaterial;
      return this;
    }

    public HomeBuilder outerWallMaterial(final String outerWallMaterial){
      this.outerWallMaterial = outerWallMaterial;
      return this;
    }

    public HomeBuilder outerWallColor(final String outerWallColor){
      this.outerWallColor = outerWallColor;
      return this;
    }

    public HomeBuilder option(final String option){
      this.option = option;
      return this;
    }

    public HomeBuilder NumberOfFloors(final int numberOfFloors){
      this.numberOfFloors = numberOfFloors;
      return this;
    }

    // build()でhomeクラスのオブジェクトを返す
    public Home build() {
      return new Home(basicMaterial, outerWallMaterial, outerWallColor, option, numberOfFloors);
    }
  }
  public static HomeBuilder builder() {
    return new HomeBuilder();
  }
}

 Builderパターンを使用したインスタンスの生成方法は以下のようになります。

  Home home1 = Home.builder()
      .NumberOfFloors(1)
      .option("ロフト付き")
      .outerWallColor("赤")
      .outerWallMaterial("モルタル")
      .build();
  
  Home home2 = Home.builder()
      .outerWallColor("青")
      .NumberOfFloors(3)
      .build();

 builder()とbuild()の間に指定したいフィールドのメソッドを指定し、引数にフイlールドの値を設定します。

 注目して欲しいのはフィールドを設定している順番です。コンストラクタを使用する場合、引数には同じ順序で値を入れないと予期せぬインスタンスになってしまいましたが、Builderパターンは順序を全く意識しなくてもインスタンスを作成できます。

 またコンストラクタを必要としないので、柔軟なインスタンスを作成することができます。

 上記の方法ではすべてのコードを自前で記述しっましたが、spring bootを使用したアプリケーションでは、「Lombok」というライブラリを使用し、クラスにアノテーションを付与するだけでbuilderのコードを自動生成してくれます。次回はspring bootでのbuilderの実装方法を紹介します。

参考

最新の投稿

SNSでもご購読できます。

コメント

コメントを残す