最近になって、主にJavaでEffective JavaのBuilderパターンを利用する機会が多かったため自分の整理用に書き留めておきます。あくまで簡単なまとめです。
#Builderパターン例
例えば、Android開発しているときにOKHttpClientを使いたいとすると、
OkHttpClient okHttpClient = new OkHttpClient();
と、インスタンスを生成しますが、ここでレスポンスをログに出力したいなと思ったら
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
としたりします。これがBuilderパターンの例です。
#何をしているのか
通常であれば、クラスは使う側がnewすることでインスタンスを生成しますが、Builderパターンの場合はちょっと違います。
クラスの中にstaticなクラスを用意してあげます。
public class Example {
private int a;
private int b;
private int c;
private int d;
private int e;
public static class ExampleBuilder {
private int a;
private int b;
private int c;
private int d;
private int e;
}
}
そして、フィールドに値をセットし、Builderそれ自身を返すメソッドをフィールドごとに生やします。
public class Example {
private int a;
private int b;
private int c;
private int d;
private int e;
public static class ExampleBuilder {
private int a;
private int b;
private int c;
private int d;
private int e;
public Builder a(int a) {
this.a = a;
return this;
}
public Builder b(int b) {
this.b = b;
return this;
}
//c, dは省略
public Builder e(int e) {
this.e = e;
return this;
}
}
}
一方そのころ、Exampleクラスにはprivateなコンストラクタを用意します。
public class Example {
private int a;
private int b;
private int c;
private int d;
private int e;
// Builderは省略
private Example(Builder builder) {
this.a = builder.a;
this.b = builder.b;
this.c = builder.c;
this.d = builder.d;
this.e = builder.e;
}
}
引数は先ほど作成したBuilderであり、そのフィールドをExampleフィールドに渡してあげます。
インナークラスなので、フィールドに直接アクセスしても大丈夫です。
そして、仕上げにBuilderにbuildメソッドを実装します。
public static class ExampleBuilder {
private int a;
private int b;
private int c;
private int d;
private int e;
public Builder a(int a) {
this.a = a;
return this;
}
public Builder b(int b) {
this.b = b;
return this;
}
//c, dは省略
public Builder e(int e) {
this.e = e;
return this;
}
public Example build() {
return new Example(this);
}
}
引数thisはBuilder自身なため、privateなコンストラクタにBuilderが渡されて、フィールドに値がセットされていれば、Exampleのフィールドに値がセットされます。
これを使用すると、Exampleを利用したいとき、
Example example = new Example.Builder.a(1).b(2).c(3).d(4).e(5).build();
とすることで、すべてのフィールドに値がセットされたExampleを取得できます。
一部にだけフィールドをセットするならば、
Example example = new Example.Builder.a(1).d(4).build();
と必要な値だけセットすれば大丈夫です。
#何がうれしいのか
個人的に感じているBuilderメソッドのメリットはコンストラクタに渡すパラメータがわかりやくすなることだと思います。
コンストラクタに渡す引数が多くなると、その分見づらくなります。
Example example = new Example(int aaaaaa, int bbbbbbb, int ccccccc, int ddddddd, int eeeeeeee);
Builderパターンで実装しておけば、何をセットしているのか明示されているため、可読性が上がります。
また、optionalなパラメータな場合、使わないときはnullをいちいち渡さなければいけません。テレスコーピングコンストラクタパターンという、コンストラクタを複数用意してあげる方法もありますが、その分読みづらくなってしまします。
Builderパターンであれば、使わないパラメータはセットしなければ済む話なので、使う側が意識することが少なくてすみます。
#終わりに
今回はEffective JavaのBuilderパターンについて書きましたが、GoFのBuilderパターンに関してもおいおい書いていきたいと思います。
#参考
ジョシュア・ブロック 著 柴田 芳樹 訳 Effective Java 第二版