Saya telah menciptakan apa yang, bagi saya, merupakan peningkatan besar di atas Pola Pembangun Josh Bloch. Tidak mengatakan dengan cara apa pun bahwa itu "lebih baik", hanya saja dalam situasi yang sangat spesifik , itu memang memberikan beberapa keuntungan - yang terbesar adalah bahwa ia memisahkan pembangun dari kelas yang akan dibangun.
Saya telah mendokumentasikan alternatif ini di bawah ini, yang saya sebut Pola Pembuat Blind.
Pola Desain: Blind Builder
Sebagai alternatif dari Joshua Bloch's Builder Pattern (item 2 di Java Efektif, edisi ke-2), saya telah menciptakan apa yang saya sebut "Blind Builder Pattern", yang berbagi banyak manfaat dari Bloch Builder dan, selain dari satu karakter, digunakan dengan cara yang persis sama. Blind Builders memiliki kelebihan
- memisahkan pembangun dari kelas terlampir, menghilangkan ketergantungan melingkar,
- sangat mengurangi ukuran kode sumber dari (apa yang tidak lagi ) kelas melampirkan, dan
- memungkinkan
ToBeBuilt
kelas untuk diperpanjang tanpa harus memperpanjang pembangunnya .
Dalam dokumentasi ini, saya akan merujuk kelas yang sedang dibangun sebagai kelas " ToBeBuilt
".
Kelas diimplementasikan dengan Bloch Builder
Bloch Builder adalah yang public static class
terkandung di dalam kelas yang dibangunnya. Sebuah contoh:
UserConfig kelas publik {
sName String pribadi akhir;
iAge akhir pribadi;
private final String sFavColor;
UserConfig publik (UserConfig.Cfg uc_c) {// CONSTRUCTOR
//transfer
coba {
sName = uc_c.sName;
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_c");
}
iAge = uc_c.iAge;
sFavColor = uc_c.sFavColor;
// VALIDASI SEMUA BIDANG DI SINI
}
public String toString () {
kembalikan "name =" + sName + ", age =" + iAge + ", sFavColor =" + sFavColor;
}
//builder...MULAI
public static class Cfg {
private String sName;
iAge pribadi int;
String pribadi sFavColor;
Cfg publik (String s_name) {
sName = s_name;
}
// setters yang kembali sendiri ... MULAI
usia Cfg publik (int i_age) {
iAge = i_age;
kembalikan ini;
}
Warna Cfg publik (String s_color) {
sFavColor = s_color;
kembalikan ini;
}
// pengaturan kembali sendiri ... AKHIR
build UserConfig publik () {
return (UserConfig baru (ini));
}
}
//builder...END
}
Instantiating kelas dengan Bloch Builder
UserConfig uc = new UserConfig.Cfg ("Kermit"). Age (50) .favoriteColor ("green"). Build ();
Kelas yang sama, diimplementasikan sebagai Blind Builder
Ada tiga bagian Blind Builder, yang masing-masingnya ada dalam file kode-sumber yang terpisah:
- The
ToBeBuilt
class (dalam contoh ini: UserConfig
)
Fieldable
Antarmukanya " "
- Pembangun
1. Kelas yang akan dibangun
Kelas yang akan dibangun menerima Fieldable
antarmuka sebagai satu-satunya parameter konstruktor. Konstruktor menetapkan semua bidang internal dari itu, dan memvalidasi masing-masing. Yang paling penting, ToBeBuilt
kelas ini tidak memiliki pengetahuan tentang pembangunnya.
UserConfig kelas publik {
sName String pribadi akhir;
iAge akhir pribadi;
private final String sFavColor;
UserConfig publik (UserConfig_Fieldable uc_f) {// CONSTRUCTOR
//transfer
coba {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f");
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
// VALIDASI SEMUA BIDANG DI SINI
}
public String toString () {
kembalikan "name =" + sName + ", age =" + iAge + ", sFavColor =" + sFavColor;
}
}
Seperti dicatat oleh satu komentator cerdas (yang secara tidak dapat dijelaskan menghapus jawaban mereka), jika ToBeBuilt
kelas juga mengimplementasikannya Fieldable
, konstruktor satu-satunya dapat digunakan baik sebagai konstruktor utama maupun salin (kelemahannya adalah bidang selalu divalidasi, meskipun diketahui bahwa bidang dalam aslinya asli ToBeBuilt
).
2. Fieldable
Antarmuka " "
Antarmuka fieldable adalah "jembatan" antara ToBeBuilt
kelas dan pembangunnya, mendefinisikan semua bidang yang diperlukan untuk membangun objek. Antarmuka ini diperlukan oleh ToBeBuilt
konstruktor kelas, dan diimplementasikan oleh pembangun. Karena antarmuka ini dapat diimplementasikan oleh kelas selain pembangun, setiap kelas dapat dengan mudah membuat instance ToBeBuilt
kelas, tanpa dipaksa untuk menggunakan pembangunnya. Ini juga memudahkan untuk memperluas ToBeBuilt
kelas, ketika memperluas pembangunnya tidak diinginkan atau diperlukan.
Seperti yang dijelaskan di bagian di bawah ini, saya tidak mendokumentasikan fungsi-fungsi di antarmuka ini sama sekali.
antarmuka publik UserConfig_Fieldable {
String getName ();
int getAge ();
String getFavoriteColor ();
}
3. Pembangun
Pembangun mengimplementasikan Fieldable
kelas. Tidak ada validasi sama sekali, dan untuk menekankan fakta ini, semua bidangnya bersifat publik dan dapat berubah. Walaupun aksesibilitas publik ini bukan keharusan, saya lebih suka dan merekomendasikannya, karena ini menegaskan kembali fakta bahwa validasi tidak terjadi sampai ToBeBuilt
konstruktor dipanggil. Ini penting, karena ada kemungkinan utas lain untuk memanipulasi pembangun lebih lanjut, sebelum diteruskan ke ToBeBuilt
konstruktor. Satu-satunya cara untuk menjamin bidang adalah valid - dengan asumsi pembangun tidak dapat "mengunci" keadaannya - adalah bagi ToBeBuilt
kelas untuk melakukan pemeriksaan terakhir.
Akhirnya, seperti halnya Fieldable
antarmuka, saya tidak mendokumentasikan getternya.
UserConfig_Cfg kelas publik mengimplementasikan UserConfig_Fieldable {
public String sName;
public int iAge;
String publik sFavColor;
UserConfig_Cfg publik (String s_name) {
sName = s_name;
}
// setters yang kembali sendiri ... MULAI
Usia UserConfig_Cfg publik (int i_age) {
iAge = i_age;
kembalikan ini;
}
UserConfig_Cfg publicColor publik (String s_color) {
sFavColor = s_color;
kembalikan ini;
}
// pengaturan kembali sendiri ... AKHIR
//getters...START
public String getName () {
return sName;
}
public int getAge () {
mengembalikan iAge;
}
public String getFavoriteColor () {
kembalikan sFavColor;
}
//getters...END
build UserConfig publik () {
return (UserConfig baru (ini));
}
}
Instantiating kelas dengan Blind Builder
UserConfig uc = new UserConfig_Cfg ("Kermit"). Age (50) .favoriteColor ("green"). Build ();
Satu-satunya perbedaan adalah " UserConfig_Cfg
" bukan " UserConfig.Cfg
"
Catatan
Kekurangan:
- Pembangun Buta tidak dapat mengakses anggota pribadi dari
ToBeBuilt
kelasnya,
- Mereka lebih bertele-tele, karena getter sekarang diperlukan baik di builder maupun di interface.
- Semuanya untuk satu kelas tidak lagi hanya di satu tempat .
Mengkompilasi Blind Builder sangat mudah:
ToBeBuilt_Fieldable
ToBeBuilt
ToBeBuilt_Cfg
The Fieldable
antarmuka sepenuhnya opsional
Untuk ToBeBuilt
kelas dengan beberapa bidang wajib - seperti UserConfig
kelas contoh ini , konstruktornya bisa saja
UserConfig publik (String s_name, int i_age, String s_favColor) {
Dan memanggil pembangun dengan
build UserConfig publik () {
return (UserConfig baru (getName (), getAge (), getFavoriteColor ()));
}
Atau bahkan dengan menghilangkan getter (di builder) sama sekali:
return (UserConfig baru (sName, iAge, sFavoriteColor));
Dengan melewati bidang secara langsung, ToBeBuilt
kelas sama "buta" (tidak menyadari pembangunnya) seperti halnya dengan Fieldable
antarmuka. Namun, untuk ToBeBuilt
kelas yang dan dimaksudkan untuk "diperluas dan disubstitusi berkali-kali" (yang ada dalam judul tulisan ini), setiap perubahan pada bidang apa pun mengharuskan perubahan di setiap sub-kelas, di setiap pembangun dan ToBeBuilt
konstruktor. Karena jumlah bidang dan subkelas meningkat, ini menjadi tidak praktis untuk dipertahankan.
(Memang, dengan beberapa bidang yang diperlukan, menggunakan pembangun sama sekali mungkin berlebihan. Bagi mereka yang tertarik, berikut adalah contoh dari beberapa antarmuka Fieldable yang lebih besar di perpustakaan pribadi saya.)
Kelas menengah dalam sub-paket
Saya memilih untuk memiliki semua pembangun dan Fieldable
kelas, untuk semua Pembuat Buta, dalam sub-paket ToBeBuilt
kelas mereka . Sub-paket selalu dinamai " z
". Ini mencegah kelas-kelas sekunder dari mengacaukan daftar paket JavaDoc. Sebagai contoh
library.class.my.UserConfig
library.class.my.z.UserConfig_Fieldable
library.class.my.z.UserConfig_Cfg
Contoh validasi
Seperti disebutkan di atas, semua validasi terjadi di ToBeBuilt
's konstruktor. Berikut ini konstruktor lagi dengan contoh kode validasi:
UserConfig publik (UserConfig_Fieldable uc_f) {
//transfer
coba {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f");
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
// validasi (harus benar-benar melakukan pre-kompilasi pola ...)
coba {
if (! Pattern.compile ("\\ w +"). matcher (sName) .matches ()) {
melempar IllegalArgumentException baru ("uc_f.getName () (\" "+ sName +" \ ") mungkin tidak kosong, dan harus berisi hanya angka digit dan garis bawah.");
}
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f.getName ()");
}
if (iAge <0) {
lempar IllegalArgumentException baru ("uc_f.getAge () (" + iAge + ") kurang dari nol.");
}
coba {
if (! Pattern.compile ("(?: red | blue | green | hot pink)"). matcher (sFavColor) .matches ()) {
melempar IllegalArgumentException baru ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" \ ") bukan merah, biru, hijau, atau hot pink.");
}
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f.getFavoriteColor ()");
}
}
Mendokumentasikan Pembangun
Bagian ini berlaku untuk Bloch Builders dan Blind Builders. Ini menunjukkan bagaimana saya mendokumentasikan kelas-kelas dalam desain ini, membuat setter (dalam pembangun) dan getter mereka (dalam ToBeBuilt
kelas) secara langsung direferensikan silang satu sama lain - dengan satu klik mouse, dan tanpa pengguna perlu tahu di mana fungsi-fungsi tersebut sebenarnya berada - dan tanpa pengembang harus mendokumentasikan apa pun secara berlebihan.
Getters: Hanya di ToBeBuilt
kelas
Getters didokumentasikan hanya di dalam ToBeBuilt
kelas. Getter yang setara baik di kelas _Fieldable
dan
_Cfg
diabaikan. Saya tidak mendokumentasikannya sama sekali.
/ **
<P> Usia pengguna. </P>
@return An int mewakili usia pengguna.
@lihat UserConfig_Cfg # age (int)
@lihat getName ()
** /
public int getAge () {
mengembalikan iAge;
}
Yang pertama @see
adalah tautan ke setter-nya, yang ada di kelas builder.
Setter: Di kelas pembangun
Setter didokumentasikan seolah-olah itu adalah di ToBeBuilt
kelas , dan juga seolah itu melakukan validasi (yang benar-benar dilakukan oleh ToBeBuilt
's konstruktor). Tanda bintang (" *
") adalah petunjuk visual yang menunjukkan bahwa target tautan ada di kelas lain.
/ **
<P> Tetapkan usia pengguna. </P>
@param i_age Mungkin tidak kurang dari nol. Dapatkan dengan {@code UserConfig # getName () getName ()} *.
@lihat #favoriteColor (String)
** /
Usia UserConfig_Cfg publik (int i_age) {
iAge = i_age;
kembalikan ini;
}
Informasi lebih lanjut
Menyatukan semuanya: Sumber lengkap contoh Blind Builder, dengan dokumentasi lengkap
UserConfig.java
import java.util.regex.Pattern;
/ **
<P> Informasi tentang pengguna - <I> [builder: UserConfig_Cfg] </I> </P>
<P> Validasi semua bidang terjadi di konstruktor kelas ini. Namun, setiap persyaratan validasi hanya dokumen dalam fungsi setter pembuat. </P>
<P> {@code java xbn.z.xmpl.lang.builder.finalv.UserConfig} </P>
** /
UserConfig kelas publik {
public static final void main (String [] igno_red) {
UserConfig uc = new UserConfig_Cfg ("Kermit"). Age (50) .favoriteColor ("green"). Build ();
System.out.println (uc);
}
sName String pribadi akhir;
iAge akhir pribadi;
private final String sFavColor;
/ **
<P> Buat instance baru. Ini menetapkan dan memvalidasi semua bidang. </P>
@param uc_f Mungkin bukan {@code null}.
** /
UserConfig publik (UserConfig_Fieldable uc_f) {
//transfer
coba {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f");
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
//mengesahkan
coba {
if (! Pattern.compile ("\\ w +"). matcher (sName) .matches ()) {
melempar IllegalArgumentException baru ("uc_f.getName () (\" "+ sName +" \ ") mungkin tidak kosong, dan harus berisi hanya angka digit dan garis bawah.");
}
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f.getName ()");
}
if (iAge <0) {
lempar IllegalArgumentException baru ("uc_f.getAge () (" + iAge + ") kurang dari nol.");
}
coba {
if (! Pattern.compile ("(?: red | blue | green | hot pink)"). matcher (sFavColor) .matches ()) {
melempar IllegalArgumentException baru ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" \ ") bukan merah, biru, hijau, atau hot pink.");
}
} catch (NullPointerException rx) {
melempar NullPointerException baru ("uc_f.getFavoriteColor ()");
}
}
//getters...START
/ **
<P> Nama pengguna. </P>
@ return A non - {@ code null}, string tidak kosong.
@lihat UserConfig_Cfg # UserConfig_Cfg (String)
@lihat #getAge ()
@lihat #getFavoriteColor ()
** /
public String getName () {
return sName;
}
/ **
<P> Usia pengguna. </P>
@ return A angka yang lebih besar dari atau sama dengan nol.
@lihat UserConfig_Cfg # age (int)
@lihat #getName ()
** /
public int getAge () {
mengembalikan iAge;
}
/ **
<P> Warna favorit pengguna. </P>
@ return A non - {@ code null}, string tidak kosong.
@lihat UserConfig_Cfg # age (int)
@lihat #getName ()
** /
public String getFavoriteColor () {
kembalikan sFavColor;
}
//getters...END
public String toString () {
return "getName () =" + getName () + ", getAge () =" + getAge () + ", getFavoriteColor () =" + getFavoriteColor ();
}
}
UserConfig_Fieldable.java
/ **
<P> Diperlukan oleh konstruktor {@link UserConfig} {@code UserConfig # UserConfig (UserConfig_Fieldable)}. </P>
** /
antarmuka publik UserConfig_Fieldable {
String getName ();
int getAge ();
String getFavoriteColor ();
}
UserConfig_Cfg.java
import java.util.regex.Pattern;
/ **
<P> Pembuat untuk {@link UserConfig}. </P>
<P> Validasi semua bidang terjadi di konstruktor <CODE> UserConfig </CODE>. Namun, setiap persyaratan validasi hanya dokumen dalam fungsi setter kelas ini. </P>
** /
UserConfig_Cfg kelas publik mengimplementasikan UserConfig_Fieldable {
public String sName;
public int iAge;
String publik sFavColor;
/ **
<P> Buat instance baru dengan nama pengguna. </P>
@param s_name Tidak boleh {@code null} atau kosong, dan hanya boleh berisi huruf, angka, dan garis bawah. Dapatkan dengan {@code UserConfig # getName () getName ()} {@ code ()} .
** /
UserConfig_Cfg publik (String s_name) {
sName = s_name;
}
// setters yang kembali sendiri ... MULAI
/ **
<P> Tetapkan usia pengguna. </P>
@param i_age Mungkin tidak kurang dari nol. Dapatkan dengan {@code UserConfig # getName () getName ()} {@ code ()} .
@lihat #favoriteColor (String)
** /
Usia UserConfig_Cfg publik (int i_age) {
iAge = i_age;
kembalikan ini;
}
/ **
<P> Tetapkan warna favorit pengguna. </P>
@param s_color Harus {@code "red"}, {@code "blue"}, {@code green}, atau {@code "hot pink"}. Dapatkan dengan {@code UserConfig # getName () getName ()} {@ code ()} *.
@lihat #age (int)
** /
UserConfig_Cfg publicColor publik (String s_color) {
sFavColor = s_color;
kembalikan ini;
}
// pengaturan kembali sendiri ... AKHIR
//getters...START
public String getName () {
return sName;
}
public int getAge () {
mengembalikan iAge;
}
public String getFavoriteColor () {
kembalikan sFavColor;
}
//getters...END
/ **
<P> Bangun UserConfig, sesuai konfigurasi. </P>
@return <CODE> (baru {@link UserConfig # UserConfig (UserConfig_Fieldable) UserConfig} (ini)) </CODE>
** /
build UserConfig publik () {
return (UserConfig baru (ini));
}
}
asImmutable
dan memasukkannya dalamReadableFoo
antarmuka [menggunakan filosofi itu, memanggilbuild
objek yang tidak dapat diubah hanya akan mengembalikan referensi ke objek yang sama].*_Fieldable
dan menambahkan getter baru ke dalamnya, dan memperpanjang*_Cfg
, dan menambahkan setter baru ke dalamnya, tapi saya tidak melihat mengapa Anda perlu mereproduksi getter dan setter yang ada. Mereka diwarisi, dan kecuali mereka membutuhkan fungsionalitas yang berbeda, tidak perlu membuatnya kembali.Saya pikir pertanyaan di sini mengasumsikan sesuatu dari awal tanpa berusaha membuktikannya, bahwa pola pembangun pada dasarnya baik.
tl; dr Saya pikir pola pembangun jarang merupakan ide yang bagus.
Tujuan pola pembangun
Tujuan dari pola pembangun adalah untuk mempertahankan dua aturan yang akan membuat konsumsi kelas Anda lebih mudah:
Objek seharusnya tidak dapat dibangun dalam keadaan tidak konsisten / tidak dapat digunakan / tidak valid.
Person
objek dapat dibangun tanpaId
diisi, sementara semua bagian kode yang menggunakan objek itu mungkin memerlukan hakId
untuk bekerja dengan benarPerson
.Konstruktor objek seharusnya tidak memerlukan terlalu banyak parameter .
Jadi tujuan dari pola pembangun adalah non-kontroversial. Saya pikir banyak keinginan dan penggunaannya didasarkan pada analisis yang pada dasarnya telah berjalan sejauh ini: Kami menginginkan dua aturan ini, ini memberikan dua aturan ini - meskipun saya pikir perlu menyelidiki cara lain untuk menyelesaikan kedua aturan itu.
Mengapa repot-repot melihat pendekatan lain?
Saya pikir alasannya ditunjukkan dengan baik oleh fakta dari pertanyaan ini sendiri; ada kerumitan dan banyak upacara ditambahkan ke struktur dalam menerapkan pola pembangun kepada mereka. Pertanyaan ini menanyakan bagaimana menyelesaikan beberapa kompleksitas itu karena seperti kompleksitas tidak, itu membuat skenario yang berperilaku aneh (mewarisi). Kompleksitas ini juga meningkatkan overhead pemeliharaan (menambah, mengubah, atau menghapus properti jauh lebih kompleks daripada yang lainnya).
Pendekatan lain
Jadi untuk aturan nomor satu di atas, pendekatan apa yang ada? Kunci aturan ini merujuk adalah bahwa pada konstruksi, sebuah objek memiliki semua informasi yang diperlukan untuk berfungsi dengan baik - dan setelah konstruksi bahwa informasi tidak dapat diubah secara eksternal (jadi itu informasi abadi).
Salah satu cara untuk memberikan semua informasi yang diperlukan ke objek pada konstruksi adalah dengan menambahkan parameter ke konstruktor. Jika informasi itu diminta oleh konstruktor, Anda tidak akan dapat membangun objek ini tanpa semua informasi itu, oleh karena itu akan dibangun menjadi keadaan yang valid. Tetapi bagaimana jika objek tersebut membutuhkan banyak informasi agar valid? Oh sial, jika itu masalahnya pendekatan ini akan melanggar aturan # 2 di atas .
Ok apa lagi yang ada? Anda dapat mengambil semua informasi yang diperlukan agar objek Anda berada dalam keadaan konsisten, dan menggabungkannya ke objek lain yang diambil pada waktu konstruksi. Kode Anda di atas alih-alih memiliki pola builder akan menjadi:
Ini tidak jauh berbeda dari pola builder, meskipun sedikit lebih sederhana, dan yang paling penting kami memenuhi aturan # 1 dan aturan # 2 sekarang .
Jadi mengapa tidak melakukan sedikit tambahan dan membuatnya menjadi pembangun penuh? Itu tidak perlu . Saya memuaskan kedua tujuan pola builder dalam pendekatan ini, dengan sesuatu yang sedikit lebih sederhana, lebih mudah untuk dipelihara, dan dapat digunakan kembali . Itu bit terakhir adalah kunci, contoh ini digunakan adalah imajiner dan tidak cocok untuk tujuan semantik dunia nyata, jadi mari kita tunjukkan bagaimana pendekatan ini menghasilkan DTO yang dapat digunakan kembali daripada kelas tujuan tunggal .
Jadi ketika Anda membangun DTO yang kohesif seperti ini, keduanya dapat memenuhi tujuan pola pembangun, lebih sederhana, dan dengan nilai / kegunaan yang lebih luas. Lebih jauh lagi, pendekatan ini memecahkan kompleksitas warisan yang dihasilkan oleh pola pembangun:
Anda mungkin menemukan DTO tidak selalu kohesif, atau untuk membuat pengelompokan properti kohesif, mereka perlu dipecah di beberapa DTO - ini bukan masalah. Jika objek Anda memerlukan 18 properti dan Anda bisa membuat 3 DTO yang kohesif dengan properti itu, Anda punya konstruksi sederhana yang memenuhi tujuan pembangun, dan kemudian beberapa. Jika Anda tidak dapat membuat pengelompokan kohesif, ini mungkin merupakan tanda bahwa objek Anda tidak kohesif jika mereka memiliki properti yang benar-benar tidak terkait - tetapi bahkan kemudian membuat satu DTO non-kohesif masih lebih disukai karena penerapan yang lebih sederhana plus menyelesaikan masalah warisan Anda.
Cara memperbaiki pola pembangun
Ok jadi semua rimble mengibas ke samping, Anda memiliki masalah dan mencari pendekatan desain untuk menyelesaikannya. Saran saya: mewarisi kelas dapat memiliki kelas bertingkat yang mewarisi dari kelas pembangun kelas super, sehingga kelas pewaris pada dasarnya memiliki struktur yang sama dengan kelas super dan memiliki pola pembangun yang harus berfungsi sama persis dengan fungsi tambahan untuk properti tambahan dari sub-kelas ..
Ketika itu ide yang bagus
Mengesampingkan, pola pembangun memiliki ceruk . Kita semua tahu itu karena kita semua mempelajari pembangun khusus ini pada satu titik atau yang lain:
StringBuilder
- di sini tujuannya bukan konstruksi sederhana, karena string tidak dapat lebih mudah untuk dibangun dan digabungkan dll. Ini adalah pembangun yang hebat karena memiliki manfaat kinerja .Karenanya, manfaat kinerjanya adalah: Anda memiliki banyak objek, mereka adalah tipe yang tidak dapat diubah, Anda perlu mengelompokkannya menjadi satu objek dari tipe yang tidak dapat diubah. Jika Anda melakukannya secara bertahap, Anda akan membuat banyak objek perantara yang dibuat di sini, jadi melakukannya sekaligus jauh lebih berkinerja dan ideal.
Jadi saya pikir kunci ketika itu adalah ide yang baik adalah dalam domain masalah
StringBuilder
: Membutuhkan untuk mengubah beberapa instance dari tipe yang tidak dapat diubah menjadi sebuah instance tunggal dari tipe yang tidak dapat diubah .sumber
fooBuilder.withBar(2).withBang("Hello").withBaz(someComplexObject).build()
menawarkan API ringkas untuk membuat foo dan dapat menawarkan pengecekan kesalahan aktual di pembangun itu sendiri. Tanpa pembangun objek itu sendiri harus memeriksa inputnya, yang berarti kita tidak lebih baik dari dulu.Fieldable
parameter. Saya akan memanggil fungsi validasi ini dariToBeBuilt
konstruktor, tetapi bisa dipanggil oleh apa saja, dari mana saja. Ini menghilangkan potensi kode redundan, tanpa memaksakan implementasi tertentu. (Dan tidak ada yang menghentikan Anda dari meneruskan bidang individual ke fungsi validasi, jika Anda tidak menyukaiFieldable
konsep - tetapi sekarang akan ada setidaknya tiga tempat di mana daftar bidang harus dipertahankan.)