Builder Pattern - Joshua Bloch - Detail
Bagaimana ide dasarnya ?
Permasalahan di artikel sebelumnya menjadi awal dari di desainnya Builder Pattern ini. Telescoping constructor, Multiple constructor, Mandatory constructor ternyata masih menyisakan masalah lain, yaitu immutability, readibility, dll.
Sehingga Joshua Bloch muncul dengan ide dasarnya berupa delegasi. Hmmm, sepertinya segala sesuatu yang berhubungan dengan Design Pattern akan berhubungan dengan Delegasi.
Yup Anda betul sekali !!
Idenya adalah membuat sebuah class tambahan yang dinamakan Builder sebagai alat bantu/delegasi untuk membuat objek tersebut.
Coba kita lihat apa permasalahan dari pendekatan sebelumnya ?
- ada nilai null yang di-passing ke constructor.
- terlalu banyak pilihan constructor.
- penggunaan setter yang berlebihan untuk nilai opsional dari sebuah objek.
- objek yang dibuat bisa diubah-ubah/mutable setelah objek dibuat.
Jadi apa yang ingin dicapai ?
- sebisa mungkin tidak ada nilai null yang di-passing ke constructor.
- seharusnya hanya satu pilihan constructor, hanya untuk atribut wajib saja.
- pakai fluent interface untuk setter atribut opsional, agar fleksibel.
- pastikan objek dibuat terakhir kali setelah semua nilai atribut terpenuhi atau lazy instantion untuk memastikan immutability.
Hmmm, cukup sulit sepertinya, tapi itulah yang diselesaikan oleh Builder Pattern ini, dengan tambahan sebuah class Builder
Jadi apa guna class tambahan Builder ini ?
Class tambahan ini digunakan sebagai penampung atribut sementara dari objek aslinya yang akan dibuat, dan akan melakukan instansiasi objek tersebut terakhir setelah semua atribut wajib dan opsional yang dibutuhkan selesai diberi nilai/value.
Apa fitur dalam class tambahan tersebut ?
- hanya mempunyai satu constructor yang parameter-parameternya terdiri dari attribute wajib.
- menggunakan fluent interface untuk setting atribut opsional nya, sehingga nantinya berbentuk method chaining.
- objek aslinya dibuat terakhir sekali, setelah semua atribut yang dibutuhkan di-set.
- merupakan static class sebagai inner class, agar tidak perlu instansiasi dua class, yaitu class aslinya, dan class buildernya. (Hal ini sebenarnya lebih ke keterbatasan bahasa Java yang tidak memungkinkan membuat class static selain di inner class)
Tentunya untuk mencapai hal tersebut, class object aslinya juga harus beradaptasi, yaitu :
- Tidak boleh diinstansiasi kecuali melalui class Builder.
Coba kita lihat implementasinya
Sebuah objek mobil dengan Builder class nya :
public class Mobil {
private String nomorStnk;
private String nomorMesin;
private String nomorRangka;
private String merekBan;
private String merekRadio;
private String merekKacaFilm;
private Integer tahunProduksi;
private Integer kapasitasCc;
private String merekJok;
// jadikan private agar tidak bisa diinstansiasi
private Mobil() {
}
// jadikan private agar tidak bisa diinstansiasi, dan hanya menerima class Builder sebagai parameternya
private Mobil(MobilBuilder builder) {
this.nomorStnk = builder.nomorStnk;
this.nomorMesin = builder.nomorMesin;
this.nomorRangka = builder.nomorRangka;
this.merekBan = builder.merekBan;
this.merekRadio = builder.merekRadio;
this.merekKacaFilm = builder.merekKacaFilm;
this.tahunProduksi = builder.tahunProduksi;
this.kapasitasCc = builder.kapasitasCc;
this.merekJok = builder.merekJok;
}
// Getter ....
// tambahkan fungsi Getter disini..
// tidak ada setter untuk class aslinya ini, untuk menjamin immutability
// inner class di set static agar cukup sekali instansiasi
public static class MobilBuilder {
private String nomorStnk;
private String nomorMesin;
private String nomorRangka;
private String merekBan;
private String merekRadio;
private String merekKacaFilm;
private Integer tahunProduksi;
private Integer kapasitasCc;
private String merekJok;
public MobilBuilder(String nomorMesin, String nomorRangka,
String merekBan, Integer kapasitasCc) {
this.nomorMesin = nomorMesin;
this.nomorRangka = nomorRangka;
this.merekBan = merekBan;
this.kapasitasCc = kapasitasCc;
}
public MobilBuilder withNomorStnk(String nomorStnk) {
this.nomorStnk = nomorStnk;
return this;
}
public MobilBuilder withMerekRadio(String merekRadio) {
this.merekRadio = merekRadio;
return this;
}
public MobilBuilder withTahunProduksi(Integer tahunProduksi) {
this.tahunProduksi = tahunProduksi;
return this;
}
public MobilBuilder withMerekJok(String merekJok) {
this.merekJok = merekJok;
return this;
}
// lakukan instansiasi terakhir, lazy instantiation
public Mobil build() {
return new Mobil(this);
}
}
}
Dan kita akan membuat beberapa objek mobil menggunakan Builder
public void createMobil() {
// instance mobil1, dengan tambahan atribut opsionalnya STNK dan tahun produksi
Mobil mobil1 = new Mobil.MobilBuilder("NM12345", "NR9876", "GoodYear", 1300).withTahunProduksi(2021)
.withNomorStnk("STNK123").build();
// instance mobil2, dengan tambahan atribut opsionalnya Tahun Produksi saja.
Mobil mobil2 = new Mobil.MobilBuilder("NM12345", "NR9876", "GoodYear", 1300).withTahunProduksi(2020).build();
}
Disini kita create objek dengan atribut wajib saja, dan melakukan set atribut opsional dengan
fluent interface seperti contoh tahunProduksi dan nomorStnk diatas. Kita tidak perlu setting merekJok, atau merekRadio karena mungkin memang tidak ada nilai. Dengan bantuan class Builder ini, maka kita bisa ignore hal tersebut, dan fokus kepada atribut wajib dan opsional yang kita miliki.