Josh Bloch'un Oluşturucu Deseni'nde benim için büyük bir gelişme olanı yarattım. Hiçbir şekilde "daha iyi" olduğunu söylememek, sadece çok özel bir durumda , bazı avantajlar sağlar - en büyüğü, kurucuyu inşa edilecek sınıfından ayırmasıdır.
Kör Oluşturucu Deseni olarak adlandırdığım bu alternatifi iyice belgeledim.
Tasarım Deseni: Kör Oluşturucu
Joshua Bloch'un Oluşturucu Paternine alternatif olarak (Etkin Java'daki 2. madde, 2. baskı), Bloch Builder'ın faydalarının çoğunu paylaşan ve tek bir karakterden başka bir şey olan "Kör Oluşturucu Paterni" dediğim şeyi yarattım. tamamen aynı şekilde kullanılır. Kör Inşaatçılar avantajı var
- yapıcıyı çevreleme sınıfından ayırmak, dairesel bir bağımlılığı ortadan kaldırmak,
- ek sınıfının kaynak kodunun boyutunu ( artık ne değildir ) büyük ölçüde azaltır ve
- sağlar
ToBeBuilt
sınıfı uzatılabilir olan kurucu uzatmak kalmadan .
Bu belgede, " ToBeBuilt
" sınıfı olarak inşa edilen sınıfa atıfta bulunacağım.
Bloch Builder ile uygulanan bir sınıf
Bloch Builder, public static class
oluşturduğu sınıfın içinde bulunur. Bir örnek:
genel sınıf UserConfig {
özel final Dize sName;
özel final int iAge;
özel final Dize sFavColor;
genel UserConfig (UserConfig.Cfg uc_c) {// CONSTRUCTOR
//Aktar
Deneyin {
sName = uc_c.sName;
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_c") at;
}
iAge = uc_c.iAge;
sFavColor = uc_c.sFavColor;
// BURADA TÜM ALANLARI DEĞERLENDİR
}
public Dize toString () {
return "name =" + sName + ", age =" + iAge + ", sFavColor =" + sFavColor;
}
//builder...START
genel statik sınıf Cfg {
özel Dize sName;
özel int iAge;
private Dize sFavColor;
public Cfg (Dize s_name) {
sName = s_name;
}
// kendini ayarlayan ayarlayıcılar ... BAŞLAT
genel CFG yaşı (int i_age) {
iAge = i_age;
bunu geri ver;
}
genel Cfg favoriteColor (Dize s_color) {
sFavColor = s_color;
bunu geri ver;
}
// kendinden dönen ayarlayıcılar ... SON
genel UserConfig build () {
return (yeni UserConfig (bu));
}
}
//builder...END
}
Bloch Builder ile bir sınıf oluşturma
UserConfig uc = yeni UserConfig.Cfg ("Kermit"). Yaş (50) .favoriteColor ("green"). Build ();
Aynı sınıf, Kör Oluşturucu olarak uygulandı
Her biri ayrı bir kaynak kodu dosyasında olan bir Kör Oluşturucu için üç bölüm vardır:
ToBeBuilt
(Bu örnekte: sınıf UserConfig
)
- Onun "
Fieldable
" arayüzü
- Oluşturucu
1. inşa edilecek sınıf
Oluşturulacak sınıf, Fieldable
arayüzünü tek yapıcı parametresi olarak kabul eder . Yapıcı, tüm iç alanları ondan ayarlar ve her birini doğrular . En önemlisi, bu ToBeBuilt
sınıfın kurucusuyla ilgili bilgisi yok.
genel sınıf UserConfig {
özel final Dize sName;
özel final int iAge;
özel final Dize sFavColor;
genel UserConfig (UserConfig_Fieldable uc_f) {// CONSTRUCTOR
//Aktar
Deneyin {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f") atma;
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
// BURADA TÜM ALANLARI DEĞERLENDİR
}
public Dize toString () {
return "name =" + sName + ", age =" + iAge + ", sFavColor =" + sFavColor;
}
}
(Açıklanamaz onların cevabını silinmiş) bir akıllı yorumcu tarafından belirtildiği gibi, eğer ToBeBuilt
sınıfı da onun uygular Fieldable
onun tek ve sadece yapıcı birincil hem olarak kullanılabilir, ve olsa bile, bir dezavantaj olduğunu alanların her zaman doğrulanır ise (kopya yapıcısı Orijinaldeki ToBeBuilt
alanların geçerli olduğu bilinmektedir ).
2. " Fieldable
" arayüzü
Fieldable arayüzü, ToBeBuilt
nesneyi oluşturmak için gerekli tüm alanları tanımlayan , sınıf ile oluşturucu arasındaki "köprüdür" . Bu arayüz, ToBeBuilt
sınıf yapıcısı tarafından talep edilir ve oluşturucu tarafından uygulanır. Bu arayüz, oluşturucu dışındaki sınıflar tarafından uygulanabileceği için, herhangi bir sınıf ToBeBuilt
, oluşturucuyu kullanmaya zorlanmadan kolayca sınıfı başlatabilir . Bu aynı zamanda ToBeBuilt
, kurucusunu genişletmek arzu edildiğinde veya gerekli olmadığında sınıfı genişletmeyi kolaylaştırır .
Aşağıdaki bölümde açıklandığı gibi, bu arayüzdeki işlevleri hiçbir şekilde belgelemiyorum.
ortak arayüz UserConfig_Fieldable {
Dize getName ();
int getAge ();
Dize getFavoriteColor ();
}
3. Oluşturucu
Oluşturucu Fieldable
sınıfı uygular . Hiç bir onaylama yapmamakta ve bu gerçeği vurgulamak için tüm alanları kamuya açık ve değişkendir. Bu halka açık erişilebilirlik bir zorunluluk olmasa da, tercih ediyorum ve tavsiye ediyorum, çünkü onayın ToBeBuilt
'kurucu' çağrılıncaya kadar gerçekleşmemesi gerçeğini yeniden uyguluyor . Bu önemlidir, çünkü başka bir dişlinin yapıcıya geçmeden önce yapıcıyı daha fazla manipüle etmesi mümkündürToBeBuilt
. Alanları garanti altına almanın tek yolu geçerlidir - yapıcının bir şekilde durumunu "kilitleyemeyeceği" varsayımı - ToBeBuilt
sınıfın son kontrolü yapması gerektiğidir.
Son olarak, Fieldable
arayüzde olduğu gibi , alıcılarından hiçbirini belgelemiyorum.
public class UserConfig_Cfg, UserConfig_Fieldable'ı uygular {
public Dize sName;
public int iAge;
public Dize sFavColor;
public UserConfig_Cfg (Dize s_name) {
sName = s_name;
}
// kendini ayarlayan ayarlayıcılar ... BAŞLAT
genel UserConfig_Cfg yaş (int i_age) {
iAge = i_age;
bunu geri ver;
}
public UserConfig_Cfg favoriteColor (Dize s_color) {
sFavColor = s_color;
bunu geri ver;
}
// kendinden dönen ayarlayıcılar ... SON
//getters...START
Genel Dize getName () {
sName döndür;
}
genel int getAge () {
iAge döndürür;
}
Genel Dize getFavoriteColor () {
sFavColor döndürür;
}
//getters...END
genel UserConfig build () {
return (yeni UserConfig (bu));
}
}
Kör Oluşturucu ile bir sınıf oluşturma
UserConfig uc = yeni UserConfig_Cfg ("Kermit"). Yaş (50) .favoriteColor ("green"). Build ();
Tek fark " UserConfig_Cfg
" yerine " UserConfig.Cfg
" dir.
notlar
Dezavantajları:
- Kör İnşaatçılar
ToBeBuilt
sınıfının özel üyelerine erişemiyor ,
- Artık hem yapımcı hem de arayüzde alıcılar gerektiğinden daha ayrıntılı.
- Tek bir sınıf için her şey artık tek bir yerde değil .
Bir Blind Builder'ı derlemek basittir:
ToBeBuilt_Fieldable
ToBeBuilt
ToBeBuilt_Cfg
Fieldable
Arayüzü tamamen isteğe bağlıdır
ToBeBuilt
Birkaç zorunlu alana sahip bir sınıf için - bu UserConfig
örnek sınıf gibi, yapıcı basit bir şekilde olabilir.
public UserConfig (Dize s_name, int i_age, Dize s_favColor) {
Ve oluşturucu ile denilen
genel UserConfig build () {
return (yeni UserConfig (getName (), getAge (), getFavoriteColor ()));
}
Veya alıcıları (inşaatçıda) tamamen ortadan kaldırarak:
return (yeni UserConfig (sName, iAge, sFavoriteColor));
Doğrudan alanları geçerek, ToBeBuilt
sınıf, Fieldable
arayüzde olduğu gibi, "oluşturucusundan haberi" kadar kördür . Bununla birlikte, ToBeBuilt
"birçok kez uzatılmış ve alt uzatılmış olması amaçlanan" (bu gönderinin başlığında) olan sınıflar için, herhangi bir alanda yapılan değişiklikler , her yapıcı ve yapıcıdaki her alt sınıfta değişiklik yapılmasını gerektirir . Alanların ve alt sınıfların sayısı arttıkça, bu sürdürülmesi pratik olmaz.ToBeBuilt
(Nitekim, birkaç gerekli alanla, bir inşaatçı kullanmak çok müstehcen olabilir. İlgilenenler için, işte kişisel kütüphanemdeki daha büyük Fieldable arayüzlerinin bir örneklemesi .)
Alt paketteki ikincil sınıflar
Fieldable
Tüm Blind Builders için tüm kurucu ve sınıfların sınıflarının bir alt paketinde olmasını tercih ediyorum ToBeBuilt
. Alt paket her zaman " z
" olarak adlandırılır . Bu, bu ikincil sınıfların JavaDoc paket listesini karıştırmasını önler. Örneğin
library.class.my.UserConfig
library.class.my.z.UserConfig_Fieldable
library.class.my.z.UserConfig_Cfg
Doğrulama örneği
Yukarıda bahsedildiği gibi, tüm validasyon işlemleri kurucuda gerçekleşir ToBeBuilt
. Örnek doğrulama koduyla tekrar yapıcı:
genel UserConfig (UserConfig_Fieldable uc_f) {
//Aktar
Deneyin {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f") atma;
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
// doğrulayın (desenleri gerçekten önceden derlemelisiniz ...)
Deneyin {
if (! Pattern.compile ("\\ w +"). eşleştirici (sName) .matches ()) {
yeni IllegalArgumentException ("uc_f.getName () (\" "+ sName +" \ ") boş bırakılamaz ve yalnızca harf rakamları ve alt çizgiler içermelidir.");
}
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f.getName ()") atın;
}
if (iAge <0) {
yeni IllegalArgumentException ("uc_f.getAge () (" + iAge + ") sıfırdan daha az.");
}
Deneyin {
if (! Pattern.compile ("(?: kırmızı | mavi | yeşil | sıcak pembe)"). eşleştirici (sFavColor) .matches ()) {
yeni IllegalArgumentException ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" \ ") kırmızı, mavi, yeşil veya pembe değildir.");
}
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f.getFavoriteColor ()") at;
}
}
Belgeleyici İnşaatçılar
Bu bölüm hem Bloch Builders hem de Blind Builders için geçerlidir. Bu tasarımdaki sınıfları nasıl belgelendiğimi, ayarlayıcıları (inşaatçıda) ve alıcılarını ( ToBeBuilt
sınıfta) doğrudan birbirine nasıl referans gösterdiğini gösteriyor - tek bir fare tıklamasıyla ve kullanıcının nerede olduğunu bilmesi gerekmiyor Bu işlevler aslında mevcuttur - ve geliştiricinin herhangi bir şeyi gereksiz yere belgelemesi gerekmez.
Alıcılar: Sadece ToBeBuilt
sınıflarda
Alıcılar sadece ToBeBuilt
sınıfta belgelenmiştir . Hem eşdeğer getters _Fieldable
ve
_Cfg
sınıflar dikkate alınmaz. Onları hiç belgelendirmiyorum.
/ **
<P> Kullanıcının yaşı. </P>
@return Kullanıcının yaşını temsil eden bir int.
@see UserConfig_Cfg # yaş (int)
@see getName ()
** /
genel int getAge () {
iAge döndürür;
}
Birincisi @see
, oluşturucu sınıfında olan setter'ına bir link.
Setleyiciler: Oluşturucu sınıfında
Setter belgelenmiştir içinde bulunduğu sanki ToBeBuilt
sınıfta ve aynı zamanda sanki o (gerçekten yapılır doğrulama yapar ToBeBuilt
'ın yapıcısı). Yıldız işareti (" *
"), bağlantının hedefinin başka bir sınıfta olduğunu gösteren görsel bir ipucudur.
/ **
<P> Kullanıcının yaşını ayarlayın. </P>
@param i_age Sıfırdan az olamaz. {@Code UserConfig # getName () getName ()} * ile alın.
@see #favoriteColor (String)
** /
genel UserConfig_Cfg yaş (int i_age) {
iAge = i_age;
bunu geri ver;
}
Daha fazla bilgi
Hepsini bir araya getirmek: Tam dokümantasyon ile birlikte Blind Builder örneğinin tam kaynağı
UserConfig.java
ithalat java.util.regex.Pattern;
/ **
<P> Bir kullanıcı hakkında bilgi - <I> [oluşturucu: UserConfig_Cfg] </I> </P>
<P> Tüm alanların doğrulanması bu sınıf kurucuda gerçekleşir. Ancak, her doğrulama gereksinimi yalnızca üreticinin belirleyici işlevlerinde belgelenir. </P>
<P> {@code java xbn.z.xmpl.lang.builder.finalv.UserConfig} </P>
** /
genel sınıf UserConfig {
genel statik son boşluk ana (Dize [] igno_red) {
UserConfig uc = yeni UserConfig_Cfg ("Kermit"). Yaş (50) .favoriteColor ("green"). Build ();
System.out.println (UC);
}
özel final Dize sName;
özel final int iAge;
özel final Dize sFavColor;
/ **
<P> Yeni bir örnek oluşturun. Bu, tüm alanları ayarlar ve doğrular. </P>
@param uc_f {@code null} olamaz.
** /
genel UserConfig (UserConfig_Fieldable uc_f) {
//Aktar
Deneyin {
sName = uc_f.getName ();
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f") atma;
}
iAge = uc_f.getAge ();
sFavColor = uc_f.getFavoriteColor ();
// validate
Deneyin {
if (! Pattern.compile ("\\ w +"). eşleştirici (sName) .matches ()) {
yeni IllegalArgumentException ("uc_f.getName () (\" "+ sName +" \ ") boş bırakılamaz ve yalnızca harf rakamları ve alt çizgiler içermelidir.");
}
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f.getName ()") atın;
}
if (iAge <0) {
yeni IllegalArgumentException ("uc_f.getAge () (" + iAge + ") sıfırdan daha az.");
}
Deneyin {
if (! Pattern.compile ("(?: kırmızı | mavi | yeşil | sıcak pembe)"). eşleştirici (sFavColor) .matches ()) {
yeni IllegalArgumentException ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" \ ") kırmızı, mavi, yeşil veya pembe değildir.");
}
} catch (NullPointerException rx) {
yeni NullPointerException ("uc_f.getFavoriteColor ()") at;
}
}
//getters...START
/ **
<P> Kullanıcının adı. </P>
@return Bir olmayan - {@ code null}, boş olmayan bir dize.
@see UserConfig_Cfg # UserConfig_Cfg (Dize)
@see #getAge ()
@see #getFavoriteColor ()
** /
Genel Dize getName () {
sName döndür;
}
/ **
<P> Kullanıcının yaşı. </P>
@return Sıfırdan büyük ya da eşit bir sayı.
@see UserConfig_Cfg # yaş (int)
@see #getName ()
** /
genel int getAge () {
iAge döndürür;
}
/ **
<P> Kullanıcının favori rengi. </P>
@return Bir olmayan - {@ code null}, boş olmayan bir dize.
@see UserConfig_Cfg # yaş (int)
@see #getName ()
** /
Genel Dize getFavoriteColor () {
sFavColor döndürür;
}
//getters...END
public Dize toString () {
return "getName () =" + getName () + ", getAge () =" + getAge () + ", getFavoriteColor () =" + getFavoriteColor ();
}
}
UserConfig_Fieldable.java
/ **
<P> {@link UserConfig} {@code UserConfig # UserConfig (UserConfig_Fieldable) yapıcısı tarafından zorunludur}. </P>
** /
ortak arayüz UserConfig_Fieldable {
Dize getName ();
int getAge ();
Dize getFavoriteColor ();
}
UserConfig_Cfg.java
ithalat java.util.regex.Pattern;
/ **
<P> {@link UserConfig} için oluşturucu. </P>
<P> Tüm alanların doğrulanması <CODE> UserConfig </CODE> yapıcısında gerçekleşir. Ancak, her bir doğrulama gereksinimi yalnızca bu sınıflar ayarlayıcı işlevlerinde belgelenir. </P>
** /
public class UserConfig_Cfg, UserConfig_Fieldable'ı uygular {
public Dize sName;
public int iAge;
public Dize sFavColor;
/ **
<P> Kullanıcı adıyla yeni bir örnek oluşturun. </P>
@param s_name {@code null} veya boş olamaz ve sadece harf, rakam ve alt çizgi içermelidir. {@Code UserConfig # getName () getName ()} {@ code ()} ile alın .
** /
public UserConfig_Cfg (Dize s_name) {
sName = s_name;
}
// kendini ayarlayan ayarlayıcılar ... BAŞLAT
/ **
<P> Kullanıcının yaşını ayarlayın. </P>
@param i_age Sıfırdan az olamaz. {@Code UserConfig # getName () getName ()} {@ code ()} ile alın .
@see #favoriteColor (String)
** /
genel UserConfig_Cfg yaş (int i_age) {
iAge = i_age;
bunu geri ver;
}
/ **
<P> Kullanıcının favori rengini ayarlayın. </P>
@param s_color {@code "red"}, {@code "blue"}, {@code green} veya {@code "hot pink"} olmalıdır. {@Code UserConfig # getName () getName ()} {@ code ()} * ile alın.
@see #age (int)
** /
public UserConfig_Cfg favoriteColor (Dize s_color) {
sFavColor = s_color;
bunu geri ver;
}
// kendinden dönen ayarlayıcılar ... SON
//getters...START
Genel Dize getName () {
sName döndür;
}
genel int getAge () {
iAge döndürür;
}
Genel Dize getFavoriteColor () {
sFavColor döndürür;
}
//getters...END
/ **
<P> UserConfig'ü yapılandırıldığı gibi oluşturun. </P>
@return <CODE> (yeni {@link UserConfig # UserConfig (UserConfig_Fieldable) UserConfig} (bu)) </CODE>
** /
genel UserConfig build () {
return (yeni UserConfig (bu));
}
}