बलोच के बिल्डर पैटर्न पर कैसे सुधार किया जाए, यह उच्च-विस्तार योग्य कक्षाओं में उपयोग के लिए अधिक उपयुक्त है


34

मैं जोशुआ बलोच के प्रभावी जावा पुस्तक (द्वितीय संस्करण) से बहुत प्रभावित हुआ हूं, शायद मैंने जितनी भी प्रोग्रामिंग पुस्तक पढ़ी है, उससे कहीं अधिक। विशेष रूप से, उनके बिल्डर पैटर्न (आइटम 2) का सबसे बड़ा प्रभाव पड़ा है।

मेरे पिछले दस वर्षों की प्रोग्रामिंग की तुलना में बलोच के निर्माता ने मुझे कुछ महीनों में बहुत दूर कर दिया, फिर भी मैं खुद को उसी दीवार से टकराता हुआ पा रहा हूं: स्व-वापसी की विधि-जंजीरों के साथ कक्षाएं बढ़ाना सबसे अच्छा हतोत्साहित करता है, और सबसे बुरा सपना है - सामान्य रूप से जब जेनरिक खेल में आते हैं, और विशेष रूप से स्व-रेफरेंशियल जेनरिक (जैसे Comparable<T extends Comparable<T>>)।

दो प्राथमिक आवश्यकताएं हैं जो मेरे पास हैं, जिनमें से केवल दूसरा मैं इस प्रश्न पर ध्यान केंद्रित करना चाहता हूं:

  1. पहली समस्या यह है कि "स्व-वापसी की विधि श्रृंखलाओं को कैसे साझा किया जाए, उन्हें हर ... एकल ... वर्ग में फिर से लागू किए बिना?" जो लोग जिज्ञासु हो सकते हैं, उनके लिए मैंने इस उत्तर-पोस्ट के निचले हिस्से में इस हिस्से को संबोधित किया है, लेकिन यह वह नहीं है जो मैं यहां ध्यान केंद्रित करना चाहता हूं।

  2. दूसरी समस्या, जो मैं टिप्पणी के लिए पूछ रहा हूं, "मैं उन कक्षाओं में एक बिल्डर को कैसे लागू कर सकता हूं जो स्वयं कई अन्य वर्गों द्वारा विस्तारित किए जाने का इरादा रखते हैं?" एक बिल्डर के साथ एक वर्ग का विस्तार स्वाभाविक रूप से बिना किसी को विस्तारित करने की तुलना में अधिक कठिन है। एक ऐसे वर्ग का विस्तार करना जिसमें एक बिल्डर है जो भी लागू करता है Needable, और इसलिए इसके साथ महत्वपूर्ण जेनेरिक जुड़ा हुआ है , वह अनिष्टकारी है।

तो यह मेरा प्रश्न है: मैं बलोच बिल्डर पर कैसे सुधार कर सकता हूं (जिसे मैं कॉल करता हूं), इसलिए मैं किसी भी वर्ग के लिए एक बिल्डर को संलग्न करने के लिए स्वतंत्र महसूस कर सकता हूं - तब भी जब उस वर्ग का मतलब "बेस क्लास" हो सकता है विस्तारित और उप-विस्तारित कई बार से अधिक-- मेरे भविष्य-स्वयं को, या मेरी लाइब्रेरी के उपयोगकर्ताओं को हतोत्साहित किए बिना , क्योंकि अतिरिक्त सामान बिल्डर (और इसके संभावित जेनरिक) उन पर थोपते हैं?


परिशिष्ट
मेरा प्रश्न ऊपर के भाग 2 पर केंद्रित है, लेकिन मैं एक समस्या पर थोड़ा विस्तार करना चाहता था, जिसमें मैंने यह भी बताया है:

पहली समस्या यह है कि "स्व-वापसी की विधि श्रृंखलाओं को कैसे साझा किया जाए, उन्हें हर ... एकल ... वर्ग में फिर से लागू किए बिना?" यह इन जंजीरों को फिर से लागू करने से वर्गों को रोकने के लिए नहीं है , जो निश्चित रूप से, उन्हें - बल्कि, गैर-उप-वर्गों को कैसे रोकना है , जो इन विधियों श्रृंखलाओं का लाभ लेना चाहते हैं, फिर से होने से -उन उपयोगकर्ताओं को उनके लिए लाभ उठाने में सक्षम होने के लिए प्रत्येक स्व-वापसी समारोह को लागू करें? इसके लिए मैं एक जरूरतमंद-जरूरतमंद डिजाइन के साथ आया हूं, जो मैं यहां के लिए इंटरफ़ेस कंकाल प्रिंट करूंगा, और इसे अभी के लिए छोड़ दूंगा। यह मेरे लिए अच्छी तरह से काम किया है (यह डिजाइन बनाने में वर्षों था ... सबसे कठिन हिस्सा परिपत्र निर्भरता से बच रहा था):

public interface Chainable  {  
    Chainable chainID(boolean b_setStatic, Object o_id);  
    Object getChainID();  
    Object getStaticChainID();  
}
public interface Needable<O,R extends Needer> extends Chainable  {
    boolean isAvailableToNeeder();
    Needable<O,R> startConfigReturnNeedable(R n_eeder);
    R getActiveNeeder();
    boolean isNeededUsable();
    R endCfg();
}
public interface Needer  {
    void startConfig(Class<?> cls_needed);
    boolean isConfigActive();
    Class getNeededType();
    void neeadableSetsNeeded(Object o_fullyConfigured);
}

जवाबों:


21

मैंने जोश के लिए जो बनाया है, वह जोश बलोच के बिल्डर पैटर्न पर एक बड़ा सुधार है। किसी भी तरह से यह कहने के लिए नहीं कि यह "बेहतर" है, बस एक बहुत ही विशिष्ट स्थिति में , यह कुछ फायदे प्रदान करता है - सबसे बड़ा यह है कि यह बिल्डर को अपने-से-निर्मित वर्ग से डिकम्पोज करता है।

मैंने नीचे इस विकल्प को अच्छी तरह से प्रलेखित किया है, जिसे मैं ब्लाइंड बिल्डर पैटर्न कहता हूं।


डिजाइन पैटर्न: ब्लाइंड बिल्डर

जोशुआ बलोच के बिल्डर पैटर्न (प्रभावी जावा, 2 संस्करण में आइटम 2) के विकल्प के रूप में, मैंने "ब्लाइंड बिल्डर पैटर्न" को कॉल किया है, जो बलोच बिल्डर के कई लाभों को साझा करता है और, एक ही चरित्र से अलग होता है। बिल्कुल उसी तरह से उपयोग किया जाता है। ब्लाइंड बिल्डर्स का फायदा है

  • बिल्डर को उसके संलग्न वर्ग से अलग करना, एक परिपत्र निर्भरता को समाप्त करना,
  • एनक्लोजिंग क्लास, और (जो अब नहीं है ) के स्रोत कोड के आकार को बहुत कम कर देता है
  • अपने बिल्डर का विस्तार किए बिनाToBeBuilt वर्ग को विस्तारित करने की अनुमति देता है ।

इस दस्तावेज़ीकरण में, मैं क्लास " ToBeBuilt" क्लास के रूप में निर्मित होने का उल्लेख करूँगा ।

बलोच बिल्डर के साथ एक वर्ग लागू किया गया

एक बलोच बिल्डर public static classउस वर्ग के अंदर निहित होता है जिसे वह बनाता है। एक उदाहरण:

सार्वजनिक वर्ग UserConfig {
   निजी अंतिम स्ट्रिंग sName;
   निजी अंतिम int iAge;
   निजी अंतिम स्ट्रिंग sFavColor;
   सार्वजनिक उपयोगकर्ताकॉन्फ़िग (UserConfig.Cfg uc_c) {// CONSTRUCTOR
      // स्थानांतरण
         प्रयत्न {
            sName = uc_c.sName;
         } पकड़ (NullPointerException rx) {
            नई NullPointerException ("uc_c") फेंकें;
         }
         iAge = uc_c.iAge;
         sFavColor = uc_c.sFavColor;
      / / यहां सभी फील्ड्स मान्य करें
   }
   सार्वजनिक स्ट्रिंग toString () {
      वापसी "नाम =" + sName + ", आयु =" + iAge + ", sFavColor =" + sFavColor;
   }
   //builder...START
   सार्वजनिक स्थिर वर्ग Cfg {
      निजी स्ट्रिंग sName;
      निजी int iAge;
      निजी स्ट्रिंग sFavColor;
      सार्वजनिक Cfg (स्ट्रिंग s_name) {
         sName = s_name;
      }
      // सेल्फ-रिटर्निंग सेटर ... START
         सार्वजनिक Cfg आयु (int i_age) {
            iAge = i_age;
            इसे वापस करो;
         }
         सार्वजनिक Cfg पसंदीदाColor (स्ट्रिंग s_color) {
            sFavColor = s_color;
            इसे वापस करो;
         }
      // सेल्फ-रिटर्निंग सेटर ... END
      सार्वजनिक UserConfig बिल्ड () {
         वापसी (नया यूजर कॉनफिग (यह));
      }
   }
   //builder...END
}

बलोच बिल्डर के साथ एक क्लास को इंस्टेंट करना

UserConfig uc = new UserConfig.Cfg ("Kermit")। आयु (50) .favoriteColor ("हरा")। Build ();

वही वर्ग, जिसे ब्लाइंड बिल्डर के रूप में लागू किया गया

ब्लाइंड बिल्डर के तीन भाग हैं, जिनमें से प्रत्येक एक अलग स्रोत-कोड फ़ाइल में है:

  1. ToBeBuiltवर्ग (इस उदाहरण में: UserConfig)
  2. इसका " Fieldable" इंटरफ़ेस
  3. बनाने वाला

1. टू-बिल्ट क्लास

टू-इन-बिल्ट क्लास अपने Fieldableइंटरफ़ेस को अपने एकमात्र निर्माता पैरामीटर के रूप में स्वीकार करता है । कंस्ट्रक्टर इसमें से सभी आंतरिक फ़ील्ड सेट करता है, और प्रत्येक को मान्य करता है। सबसे महत्वपूर्ण बात, इस ToBeBuiltवर्ग को अपने बिल्डर का कोई ज्ञान नहीं है।

सार्वजनिक वर्ग UserConfig {
   निजी अंतिम स्ट्रिंग sName;
   निजी अंतिम int iAge;
   निजी अंतिम स्ट्रिंग sFavColor;
    सार्वजनिक UserConfig (UserConfig_Fieldable uc_f) {// CONSTRUCTOR
      // स्थानांतरण
         प्रयत्न {
            sName = uc_f.getName ();
         } पकड़ (NullPointerException rx) {
            नई NullPointerException ("uc_f") फेंकें;
         }
         iAge = uc_f.getAge ();
         sFavColor = uc_f.getFavoriteColor ();
      / / यहां सभी फील्ड्स मान्य करें
   }
   सार्वजनिक स्ट्रिंग toString () {
      वापसी "नाम =" + sName + ", आयु =" + iAge + ", sFavColor =" + sFavColor;
   }
}

जैसा कि एक स्मार्ट टिप्पणीकार (जो उनके उत्तर को अनावश्यक रूप से हटा दिया गया है) द्वारा नोट किया गया है, यदि ToBeBuiltवर्ग भी इसका कार्यान्वयन करता है Fieldable, तो इसके एक-और-केवल निर्माता को इसके प्राथमिक और प्रतिलिपि निर्माता दोनों के रूप में उपयोग किया जा सकता है (एक नुकसान यह है कि फ़ील्ड हमेशा मान्य होते हैं, भले ही यह ज्ञात है कि मूल में फ़ील्ड ToBeBuiltमान्य हैं)।

2. " Fieldable" इंटरफ़ेस

ToBeBuiltफ़ील्ड और उसके बिल्डर के बीच फील्डेबल इंटरफ़ेस "ब्रिज" है, जो ऑब्जेक्ट को बनाने के लिए आवश्यक सभी फ़ील्ड्स को परिभाषित करता है। यह इंटरफ़ेस ToBeBuiltवर्गों के निर्माता द्वारा आवश्यक है , और बिल्डर द्वारा कार्यान्वित किया जाता है। चूंकि यह इंटरफ़ेस बिल्डर के अलावा अन्य वर्गों द्वारा कार्यान्वित किया जा सकता है, कोई भी वर्ग ToBeBuiltअपने बिल्डर का उपयोग करने के लिए मजबूर किए बिना, आसानी से क्लास को तुरंत रद्द कर सकता है । इससे ToBeBuiltवर्ग का विस्तार करना भी आसान हो जाता है , जब इसके बिल्डर का विस्तार करना वांछनीय या आवश्यक नहीं है।

जैसा कि एक नीचे अनुभाग में वर्णित है, मैं इस इंटरफ़ेस में फ़ंक्शन को बिल्कुल भी दस्तावेज़ नहीं करता हूं।

सार्वजनिक इंटरफ़ेस UserConfig_Fieldable {
   स्ट्रिंग getName ();
   int getAge ();
   स्ट्रिंग getFavoriteColor ();
}

3. बिल्डर

बिल्डर Fieldableक्लास को लागू करता है । यह बिल्कुल भी मान्य नहीं है, और इस तथ्य पर जोर देने के लिए, इसके सभी क्षेत्र सार्वजनिक और पारस्परिक हैं। हालांकि यह सार्वजनिक पहुंच की आवश्यकता नहीं है, मैं इसे पसंद करता हूं और इसकी सिफारिश करता हूं, क्योंकि यह इस तथ्य को फिर से लागू करता है कि सत्यापन तब तक नहीं होता है जब तक कि ToBeBuiltनिर्माता को नहीं बुलाया जाता है। यह महत्वपूर्ण है, क्योंकि बिल्डर को हेरफेर करने के लिए एक और धागा के लिए संभव है , इससे पहले कि यह ToBeBuiltकंस्ट्रक्टर को पास कर दिया जाए । खेतों की गारंटी देने का एकमात्र तरीका वैध है - यह मानकर कि बिल्डर किसी तरह अपने राज्य को "लॉक" नहीं कर सकता है - ToBeBuiltवर्ग के लिए अंतिम जांच करने के लिए है।

अंत में, Fieldableइंटरफ़ेस के साथ की तरह , मैं इसके किसी भी गेटर्स को दस्तावेज नहीं करता।

सार्वजनिक वर्ग UserConfig_Cfg उपयोक्ता उपयोक्ताConfig_Fieldable {
   सार्वजनिक स्ट्रिंग sName;
   सार्वजनिक int iAge;
    सार्वजनिक स्ट्रिंग sFavColor;
    सार्वजनिक UserConfig_Cfg (स्ट्रिंग s_name) {
       sName = s_name;
    }
    // सेल्फ-रिटर्निंग सेटर ... START
       सार्वजनिक UserConfig_Cfg आयु (int i_age) {
          iAge = i_age;
          इसे वापस करो;
       }
       सार्वजनिक UserConfig_Cfg पसंदीदाकाला (स्ट्रिंग s_color) {
          sFavColor = s_color;
          इसे वापस करो;
       }
    // सेल्फ-रिटर्निंग सेटर ... END
    //getters...START
       सार्वजनिक स्ट्रिंग getName () {
          वापसी sName;
       }
       सार्वजनिक int getAge () {
          वापसी iAge;
       }
       सार्वजनिक स्ट्रिंग getFavoriteColor () {
          वापसी sFavColor;
       }
    //getters...END
    सार्वजनिक UserConfig बिल्ड () {
       वापसी (नया यूजर कॉनफिग (यह));
    }
}

ब्लाइंड बिल्डर के साथ एक क्लास को इंस्टेंट करना

UserConfig uc = new UserConfig_Cfg ("Kermit")। आयु (50) .favoriteColor ("हरा")। build ();

एकमात्र अंतर " UserConfig_Cfg" के बजाय " UserConfig.Cfg" है

टिप्पणियाँ

नुकसान:

  • ब्लाइंड बिल्डर्स अपने ToBeBuiltवर्ग के निजी सदस्यों तक नहीं पहुँच सकते हैं ,
  • वे अधिक क्रियात्मक हैं, क्योंकि बिल्डर और इंटरफ़ेस दोनों में गेटर्स की आवश्यकता होती है।
  • एक एकल वर्ग के लिए सब कुछ अब केवल एक स्थान पर नहीं है

एक ब्लाइंड बिल्डर संकलित करना सीधे-आगे है:

  1. ToBeBuilt_Fieldable
  2. ToBeBuilt
  3. ToBeBuilt_Cfg

Fieldableइंटरफ़ेस पूरी तरह से वैकल्पिक है

ToBeBuiltकुछ आवश्यक क्षेत्रों के साथ एक वर्ग के लिए - जैसे कि यह UserConfigउदाहरण वर्ग, कंस्ट्रक्टर बस हो सकता है

सार्वजनिक UserConfig (स्ट्रिंग s_name, int i_age, स्ट्रिंग s_favColor) {

और बिल्डर के साथ में बुलाया

सार्वजनिक UserConfig बिल्ड () {
   वापसी (नया UserConfig (getName) (), getAge (), getFavoriteColor ());
}

या यहां तक ​​कि गेटर्स को खत्म करके (बिल्डर में) पूरी तरह से:

   वापसी (नया यूजर कॉनफिग (sName, iAge, sFavoriteColor));

सीधे खेतों से गुजरते हुए, ToBeBuiltक्लास "अंधा" (इसके निर्माता से अनजान) के रूप में यह Fieldableइंटरफ़ेस के साथ है। हालाँकि, उन ToBeBuiltवर्गों के लिए जिनका उद्देश्य "कई बार विस्तारित और उप-विस्तारित" होना है (जो कि इस पद के शीर्षक में है), किसी भी क्षेत्र में किसी भी बदलाव के लिए हर उप-वर्ग में, प्रत्येक बिल्डर और निर्माता में परिवर्तन आवश्यक हैं ToBeBuilt। जैसे-जैसे खेतों और उप-वर्गों की संख्या बढ़ती है, यह बनाए रखने के लिए अव्यावहारिक हो जाता है।

(वास्तव में, कुछ आवश्यक क्षेत्रों के साथ, एक बिल्डर का उपयोग करना अधिक हो सकता है। जो लोग रुचि रखते हैं, उनके लिए यहां मेरी निजी लाइब्रेरी में कुछ बड़े फील्डेबल इंटरफेस का नमूना है।)

उप-पैकेज में माध्यमिक कक्षाएं

मैं सभी बिल्डर और Fieldableवर्गों को चुनता हूं , सभी ब्लाइंड बिल्डर्स के लिए, उनकी ToBeBuiltकक्षा के एक उप-पैकेज में । उप-पैकेज को हमेशा " z" नाम दिया जाता है । यह इन द्वितीयक कक्षाओं को JavaDoc पैकेज सूची को अव्यवस्थित करने से रोकता है। उदाहरण के लिए

  • library.class.my.UserConfig
  • library.class.my.z.UserConfig_Fieldable
  • library.class.my.z.UserConfig_Cfg

वैधता उदाहरण

जैसा कि ऊपर उल्लेख किया गया है, सभी सत्यापन ToBeBuiltनिर्माणकर्ता में होते हैं। यहाँ उदाहरण सत्यापन कोड के साथ फिर से निर्माता है:

सार्वजनिक UserConfig (UserConfig_Fieldable uc_f) {
   // स्थानांतरण
      प्रयत्न {
         sName = uc_f.getName ();
      } पकड़ (NullPointerException rx) {
         नई NullPointerException ("uc_f") फेंकें;
      }
      iAge = uc_f.getAge ();
      sFavColor = uc_f.getFavoriteColor ();
   // सत्यापित करें (वास्तव में पैटर्न को पूर्व-संकलित करना चाहिए ...)
      प्रयत्न {
         अगर (पैटर्न। pile ("\\ w +")। मिलान करने वाला (नाम) .matches ()! {
            नए IllegalArgumentException को फेंकें ("uc_f.getName () (\" "+ sName +" \ ") खाली नहीं हो सकता है, और इसमें केवल अक्षर अंक और अंडरस्कोर शामिल होने चाहिए।");
         }
      } पकड़ (NullPointerException rx) {
         नया NullPointerException ("uc_f.getName ()") फेंकें;
      }
      अगर (iAge <0) {
         नई IllegalArgumentException फेंकें ("uc_f.getAge () (" + iAge + ") शून्य से कम है।");
      }
      प्रयत्न {
         अगर (पैटर्न.कॉमपाइल ("(?: रेड | ब्लू | ग्रीन | हॉट पिंक)")। मैचर (sFavColor) .matches ()) {
            नई IllegalArgumentException फेंकें ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" "\") लाल, नीला, हरा या गर्म गुलाबी नहीं है।));
         }
      } पकड़ (NullPointerException rx) {
         नए NullPointerException ("uc_f.getFavoriteColor ()") को फेंक दें;
      }
}

डॉक्यूमेंटेशन बिल्डर्स

यह खंड बलोच बिल्डर्स और ब्लाइंड बिल्डर्स दोनों के लिए लागू है। यह प्रदर्शित करता है कि मैं इस डिजाइन में कक्षाओं का दस्तावेज कैसे बनाता हूं, (बिल्डर में) और उनके गेटर्स ( ToBeBuiltकक्षा में) सीधे एक-दूसरे को क्रॉस-रेफ़र करते हैं - एक माउस-क्लिक के साथ, और उपयोगकर्ता को यह जानने की आवश्यकता के बिना कि कहां है उन कार्यों वास्तव में रहते हैं - और डेवलपर के बिना कुछ भी अनावश्यक रूप से दस्तावेज़ करने के लिए।

गेटर्स: ToBeBuiltकेवल कक्षाओं में

गेट्स को केवल ToBeBuiltकक्षा में प्रलेखित किया जाता है । दोनों वर्गों _Fieldableऔर_Cfg वर्गों में बराबर प्राप्तकर्ताओं को अनदेखा किया जाता है। मैं उन्हें बिल्कुल भी दस्तावेज नहीं देता।

/ **
   <P> उपयोगकर्ता की आयु। </ P>
   @ ग्रेट उपयोगकर्ता की उम्र का प्रतिनिधित्व करने वाला एक इंटर्न।
   @ हम उपयोगकर्ता कांफिग_फैग # उम्र (इंट)
   @ वह getName ()
 ** /
सार्वजनिक int getAge () {
   वापसी iAge;
}

पहला @seeइसके सेटर का लिंक है, जो बिल्डर क्लास में है।

सेटर्स: बिल्डर-क्लास में

सेटर का दस्तावेजीकरण कियाToBeBuilt जाता है जैसे कि वह कक्षा में है , और यह भी मानो कि वह सत्यापन करता है (जो वास्तव में ToBeBuiltनिर्माणकर्ता द्वारा किया जाता है )। तारांकन चिह्न (" *") एक दृश्य सुराग है जो दर्शाता है कि लिंक का लक्ष्य किसी अन्य वर्ग में है।

/ **
   <P> उपयोगकर्ता की आयु निर्धारित करें। </ P>
   @ अपरम__ शून्य से कम नहीं हो सकता। {@Code UserConfig # getName () getName ()} * के साथ जाओ।
   @ वे #favoriteColor (स्ट्रिंग)
 ** /
सार्वजनिक UserConfig_Cfg आयु (int i_age) {
   iAge = i_age;
   इसे वापस करो;
}

अग्रिम जानकारी

इसे एक साथ रखना: ब्लाइंड बिल्डर उदाहरण का पूरा स्रोत, पूर्ण प्रलेखन के साथ

UserConfig.java

आयात java.util.regex.Pattern;
/ **
   <P> किसी उपयोगकर्ता के बारे में जानकारी - <I> [बिल्डर: UserConfig_Cfg] </ I> / />
   <P> सभी वर्गों का सत्यापन इस वर्ग के निर्माणकर्ता में होता है। हालांकि, प्रत्येक सत्यापन की आवश्यकता केवल बिल्डर के सेटर कार्यों में दस्तावेज है। </ P>
   <P> {@ कोड जावा xbn.z.xmpl.lang.builder.finalv.UserConfig} </ P>
 ** /
सार्वजनिक वर्ग UserConfig {
   सार्वजनिक स्थिर अंतिम शून्य मुख्य (स्ट्रिंग [] igno_red) {
      UserConfig uc = new UserConfig_Cfg ("Kermit")। आयु (50) .favoriteColor ("हरा")। build ();
      Println (यूसी);
   }
   निजी अंतिम स्ट्रिंग sName;
   निजी अंतिम int iAge;
   निजी अंतिम स्ट्रिंग sFavColor;
   / **
      <P> एक नया उदाहरण बनाएं। यह सभी क्षेत्रों को सेट और मान्य करता है। </ P>
      @param uc_f {@code null} नहीं हो सकता।
    ** /
   सार्वजनिक UserConfig (UserConfig_Fieldable uc_f) {
      // स्थानांतरण
         प्रयत्न {
            sName = uc_f.getName ();
         } पकड़ (NullPointerException rx) {
            नई NullPointerException ("uc_f") फेंकें;
         }
         iAge = uc_f.getAge ();
         sFavColor = uc_f.getFavoriteColor ();
      // सत्यापित करें
         प्रयत्न {
            अगर (पैटर्न। pile ("\\ w +")। मिलान करने वाला (नाम) .matches ()! {
               नए IllegalArgumentException को फेंकें ("uc_f.getName () (\" "+ sName +" \ ") खाली नहीं हो सकता है, और इसमें केवल अक्षर अंक और अंडरस्कोर शामिल होने चाहिए।");
            }
         } पकड़ (NullPointerException rx) {
            नया NullPointerException ("uc_f.getName ()") फेंकें;
         }
         अगर (iAge <0) {
            नई IllegalArgumentException फेंकें ("uc_f.getAge () (" + iAge + ") शून्य से कम है।");
         }
         प्रयत्न {
            अगर (पैटर्न.कॉमपाइल ("(?: रेड | ब्लू | ग्रीन | हॉट पिंक)")। मैचर (sFavColor) .matches ()) {
               नई IllegalArgumentException फेंकें ("uc_f.getFavoriteColor () (\" "+ uc_f.getFavoriteColor () +" "\") लाल, नीला, हरा या गर्म गुलाबी नहीं है।));
            }
         } पकड़ (NullPointerException rx) {
            नए NullPointerException ("uc_f.getFavoriteColor ()") को फेंक दें;
         }
   }
   //getters...START
      / **
         <P> उपयोगकर्ता का नाम। </ P>
         @ ग्रेट एक गैर - {@ कोड नल}, गैर-रिक्त स्ट्रिंग।
         @see UserConfig_Cfg # UserConfig_Cfg (स्ट्रिंग)
         @ हम # आगा ()
         @ वह #getFavoriteColor ()
       ** /
      सार्वजनिक स्ट्रिंग getName () {
         वापसी sName;
      }
      / **
         <P> उपयोगकर्ता की आयु। </ P>
         @ ग्रेट एक संख्या से अधिक-से-या-बराबर-शून्य।
         @ हम उपयोगकर्ता कांफिग_फैग # उम्र (इंट)
         @ हम # भूलनाम ()
       ** /
      सार्वजनिक int getAge () {
         वापसी iAge;
      }
      / **
         <P> उपयोगकर्ता का पसंदीदा रंग। </ P>
         @ ग्रेट एक गैर - {@ कोड नल}, गैर-रिक्त स्ट्रिंग।
         @ हम उपयोगकर्ता कांफिग_फैग # उम्र (इंट)
         @ हम # भूलनाम ()
       ** /
      सार्वजनिक स्ट्रिंग getFavoriteColor () {
         वापसी sFavColor;
      }
   //getters...END
   सार्वजनिक स्ट्रिंग toString () {
      वापसी "getName () =" + getName () + ", getAge () =" + getAge () + ", getFavoriteColor () =" + getFavoriteColor ();
   }
}

UserConfig_Fieldable.java

/ **
   <P> {@link UserConfig} {@code UserConfig # UserConfig (UserConfig_Fieldable) कंस्ट्रक्टर} द्वारा आवश्यक है। </ P>
 ** /
सार्वजनिक इंटरफ़ेस UserConfig_Fieldable {
   स्ट्रिंग getName ();
   int getAge ();
   स्ट्रिंग getFavoriteColor ();
}

UserConfig_Cfg.java

आयात java.util.regex.Pattern;
/ **
   <P> बिल्डर के लिए {@link UserConfig}। </ P>
   <P> सभी क्षेत्रों का सत्यापन <CODE> UserConfig </ CODE> कंस्ट्रक्टर में होता है। हालाँकि, प्रत्येक सत्यापन की आवश्यकता केवल इस वर्ग सेटर कार्यों में ही है। </ P>
 ** /
सार्वजनिक वर्ग UserConfig_Cfg उपयोक्ता उपयोक्ताConfig_Fieldable {
   सार्वजनिक स्ट्रिंग sName;
   सार्वजनिक int iAge;
   सार्वजनिक स्ट्रिंग sFavColor;
   / **
      <P> उपयोगकर्ता के नाम के साथ एक नया उदाहरण बनाएं। </ P>
      @param__name {@code null} या रिक्त नहीं हो सकता है, और इसमें केवल अक्षर, अंक और अंडरस्कोर शामिल होने चाहिए। {@Code UserConfig # getName () getName ()} {@ कोड ()} के साथ प्राप्त करें ।
    ** /
   सार्वजनिक UserConfig_Cfg (स्ट्रिंग s_name) {
      sName = s_name;
   }
   // सेल्फ-रिटर्निंग सेटर ... START
      / **
         <P> उपयोगकर्ता की आयु निर्धारित करें। </ P>
         @ अपरम__ शून्य से कम नहीं हो सकता। {@Code UserConfig # getName () getName ()} {@ कोड ()} के साथ प्राप्त करें ।
         @ वे #favoriteColor (स्ट्रिंग)
       ** /
      सार्वजनिक UserConfig_Cfg आयु (int i_age) {
         iAge = i_age;
         इसे वापस करो;
      }
      / **
         <P> उपयोगकर्ता का पसंदीदा रंग सेट करें। </ P>
         @param__color {@code "लाल"}, {@code "नीला"}, {@code हरा}, या {@code "गर्म गुलाबी"} होना चाहिए। {@Code UserConfig # getName () getName ()} {@ कोड ()} * के साथ प्राप्त करें।
         @ वे # चरण (इंट)
       ** /
      सार्वजनिक UserConfig_Cfg पसंदीदाकाला (स्ट्रिंग s_color) {
         sFavColor = s_color;
         इसे वापस करो;
      }
   // सेल्फ-रिटर्निंग सेटर ... END
   //getters...START
      सार्वजनिक स्ट्रिंग getName () {
         वापसी sName;
      }
      सार्वजनिक int getAge () {
         वापसी iAge;
      }
      सार्वजनिक स्ट्रिंग getFavoriteColor () {
         वापसी sFavColor;
      }
   //getters...END
   / **
      <P> कॉन्फ़िगर के रूप में UserConfig बनाएँ। </ P>
      @return <CODE> (नया {@link UserConfig # UserConfig (UserConfig_Fieldable) UserConfig} (यह)) </ CODE>
    ** /
   सार्वजनिक UserConfig बिल्ड () {
      वापसी (नया यूजर कॉनफिग (यह));
   }
}


1
निश्चित रूप से, यह एक सुधार है। द बलोच बिल्डर, जैसा कि यहां लागू किया गया है, दो ठोस वर्गों को जोड़े , ये एक -से- एक और इसके बिल्डर हैं। यह खराब डिजाइन है प्रति से । आप जिस ब्लाइंड बिल्डर का वर्णन करते हैं, उस कपलिंग को टू-बी-बिल्ट क्लास होने के कारण इसकी निर्माण निर्भरता को एक अमूर्तता के रूप में परिभाषित करता है , जिसे अन्य वर्ग डिकम्पोज्ड फैशन में लागू कर सकते हैं। आपने बहुत ही आवश्यक रूप से लागू किया है जो एक आवश्यक वस्तु-उन्मुख डिजाइन दिशानिर्देश है।
rucamzu

3
यदि आप पहले से ही एल्गोरिथ्म डिजाइन का अच्छा टुकड़ा नहीं है, तो आपको वास्तव में इस बारे में ब्लॉग करना चाहिए! मैं इसे अभी साझा करना बंद कर रहा हूं :-)।
मार्टिज़न वेरबर्ग

4
आपके दयालु शब्द के लिए धन्यवाद। यह अब मेरे नए ब्लॉग पर पहली पोस्ट है: aliteralmind.wordpress.com/2014/02/14/blind_builder
aliteralmind

यदि बिल्डर और निर्मित ऑब्जेक्ट दोनों फील्डेबल को लागू करते हैं, तो पैटर्न एक से मिलता जुलता है, जिसे मैंने ReadableFoo / MutableFoo / ImmutableFoo के रूप में संदर्भित किया है, हालांकि एक परिवर्तनशील चीज बनाने की विधि होने के बजाय "बिल्डर का सदस्य" हूं, इसे कॉल करें asImmutableऔर इसे ReadableFooइंटरफ़ेस में शामिल करें [उस दर्शन का उपयोग करके, buildएक अपरिवर्तनीय वस्तु पर कॉल करना बस उसी वस्तु का एक संदर्भ लौटाएगा]।
सुपरकैट

1
@ThomasN आपको इसमें *_Fieldableनए गेटर्स को बढ़ाने और जोड़ने की जरूरत है , और इसका विस्तार करने के लिए *_Cfgऔर इसमें नए सेटर्स जोड़ने की जरूरत है, लेकिन मैं यह नहीं देखता कि आपको मौजूदा गेटर्स और सेटर्स को पुन: पेश करने की आवश्यकता क्यों होगी। उन्हें विरासत में मिला है, और जब तक उन्हें अलग-अलग कार्यक्षमता की आवश्यकता नहीं है, तब तक उन्हें फिर से बनाने की कोई आवश्यकता नहीं है।
aliteralmind

13

मुझे लगता है कि यहां सवाल यह साबित करने की कोशिश किए बिना शुरू से ही कुछ है, कि बिल्डर पैटर्न स्वाभाविक रूप से अच्छा है।

मुझे लगता है कि बिल्डर पैटर्न शायद ही कभी एक अच्छा विचार है।


बिल्डर पैटर्न उद्देश्य

बिल्डर पैटर्न का उद्देश्य दो नियमों को बनाए रखना है जो आपकी कक्षा का उपभोग करना आसान बनाएंगे:

  1. असंगत / अनुपयोगी / अमान्य राज्यों में वस्तुओं का निर्माण नहीं किया जाना चाहिए।

    • इस परिदृश्य जहां उदाहरण के लिए एक को संदर्भित करता है Personवस्तु यह है बिना निर्माण किया जा सकता है Id, में भरा है, जबकि कोड के सभी टुकड़ों है कि उपयोग उस वस्तु हो सकता है की आवश्यकता होती हैId केवल करने के लिए ठीक से साथ काम Person
  2. ऑब्जेक्ट कंस्ट्रक्टर को बहुत अधिक मापदंडों की आवश्यकता नहीं होनी चाहिए ।

तो बिल्डर पैटर्न का उद्देश्य गैर-विवादास्पद रूप से अच्छा है। मुझे लगता है कि इसकी बहुत इच्छा और उपयोग विश्लेषण पर आधारित है जो मूल रूप से इस दूर चला गया है: हम इन दो नियमों को चाहते हैं, इससे ये दो नियम मिलते हैं - हालांकि मुझे लगता है कि यह उन दो नियमों को पूरा करने के अन्य तरीकों की जांच के लायक है।


अन्य दृष्टिकोणों को देखकर क्यों परेशान होते हैं?

मुझे लगता है कि इस सवाल के तथ्य के कारण को अच्छी तरह से दिखाया गया है; जटिलता और बहुत सारे समारोह हैं जो उन्हें बिल्डर पैटर्न को लागू करने के लिए संरचनाओं में जोड़ा गया है। यह सवाल पूछ रहा है कि उस जटिलता का कुछ कैसे हल किया जाए क्योंकि जैसा कि जटिलता की होती है, यह ऐसे परिदृश्य के लिए बनाता है जो अजीब तरीके से व्यवहार करता है (विरासत में मिला)। इस जटिलता से रखरखाव ओवरहेड भी बढ़ जाता है (संपत्तियों को जोड़ना, बदलना या हटाना, अन्यथा की तुलना में कहीं अधिक जटिल है)।


अन्य दृष्टिकोण

तो नियम नंबर एक के लिए ऊपर, क्या दृष्टिकोण हैं? यह नियम इस बात का उल्लेख कर रहा है कि निर्माण के समय, एक वस्तु में वह सभी जानकारी होती है, जिसे उसे ठीक से काम करने की आवश्यकता होती है - और निर्माण के बाद उस जानकारी को बाहरी रूप से नहीं बदला जा सकता है (इसलिए यह अपरिवर्तनीय जानकारी है)।

निर्माण में किसी वस्तु को सभी आवश्यक जानकारी देने का एक तरीका बस निर्माणकर्ता के लिए पैरामीटर जोड़ना है। यदि निर्माणकर्ता द्वारा उस जानकारी की मांग की जाती है, तो आप इस जानकारी के बिना इस वस्तु का निर्माण नहीं कर पाएंगे, इसलिए इसका निर्माण एक वैध स्थिति में किया जाएगा। लेकिन क्या होगा यदि ऑब्जेक्ट को मान्य होने के लिए बहुत अधिक जानकारी की आवश्यकता है? अरे खतरा, अगर ऐसा है तो यह दृष्टिकोण नियम # 2 को तोड़ देगा

ठीक है और क्या है? ठीक है, आप बस उस सभी जानकारी को ले सकते हैं जो आपके ऑब्जेक्ट के लिए एक सुसंगत स्थिति में होना आवश्यक है, और इसे एक अन्य ऑब्जेक्ट में बंडल करें जो कि निर्माण के समय लिया जाता है। बिल्डर पैटर्न होने के बजाय आपका कोड ऊपर होगा:

//DTO...START
public class Cfg  {
   public String sName    ;
   public int    iAge     ;
   public String sFavColor;
}
//DTO...END

public class UserConfig  {
   private final String sName    ;
   private final int    iAge     ;
   private final String sFavColor;
   public UserConfig(Cfg uc_c)  {
      ...
   }

   public String toString()  {
      return  "name=" + sName + ", age=" + iAge + ", sFavColor=" + sFavColor;
   }
}

यह बिल्डर पैटर्न से भिन्न नहीं है, हालांकि यह थोड़ा सरल है, और सबसे महत्वपूर्ण बात यह है कि हम नियम # 1 को संतुष्ट कर रहे हैं और अब इस नियम को "2" कर रहे हैं

तो क्यों न थोड़ा अतिरिक्त करें और इसे बिल्डर पर पूर्ण बनाएं? यह केवल अनावश्यक है । मैंने इस दृष्टिकोण में बिल्डर पैटर्न के दोनों उद्देश्यों को संतुष्ट किया, कुछ सरल, बनाए रखने में आसान और पुन: प्रयोज्य । वह अंतिम बिट कुंजी है, जिसका उपयोग किया जा रहा है यह उदाहरण काल्पनिक है और खुद को वास्तविक-दुनिया शब्दार्थ के उद्देश्य से उधार नहीं देता है, तो आइए दिखाते हैं कि यह दृष्टिकोण एकल उद्देश्य वर्ग के बजाय पुन: प्रयोज्य डीटीओ में कैसे परिणाम देता है

public class NetworkAddress {
   public String Ip;
   public int Port;
   public NetworkAddress Proxy;
}

public class SocketConnection {
   public SocketConnection(NetworkAddress address) {
      ...
   }
}

public class FtpClient {
   public FtpClient(NetworkAddress address) {
      ...
   }
}

तो जब आप का निर्माण जोड़नेवाला इस तरह DTOs, वे दोनों बिल्डर पैटर्न का उद्देश्य, अधिक बस, और व्यापक मूल्य / उपयोगिता के साथ पूरा कर सकते हैं। इसके अलावा इस दृष्टिकोण को विरासत की जटिलता को हल करता है बिल्डर पैटर्न में परिणाम:

public class SslCert {
   public NetworkAddress Authority;
   public byte[] PrivateKey;
   public byte[] PublicKey;
}

public class FtpsClient extends FtpClient {
   public FtpsClient(NetworkAddress address, SslCert cert) {
      super(address);
      ...
   }
}

आप पा सकते हैं डीटीओ हमेशा सामंजस्यपूर्ण नहीं होता है, या संपत्तियों के समूह बनाने के लिए उन्हें कई डीटीओ में तोड़ा जाना चाहिए - यह वास्तव में समस्या नहीं है। यदि आपकी वस्तु को 18 गुणों की आवश्यकता है और आप उन गुणों के साथ 3 कोसिविव डीटीओ बना सकते हैं, तो आपको एक सरल निर्माण मिला है जो बिल्डरों के उद्देश्यों को पूरा करता है, और फिर कुछ। यदि आप कोआपरेटिव ग्रुपिंग के साथ नहीं आ सकते हैं, तो यह एक संकेत हो सकता है कि आपके ऑब्जेक्ट सामंजस्यपूर्ण नहीं हैं यदि उनके पास ऐसे गुण हैं जो पूरी तरह से असंबंधित हैं - लेकिन फिर भी एक भी गैर-कोऑसिविव डीटीओ बनाना सरल कार्यान्वयन के कारण अभी भी बेहतर है अपनी विरासत की समस्या को हल करना।


बिल्डर पैटर्न में सुधार कैसे करें

ठीक है, इसलिए सभी अलग-अलग चीर-फाड़ कर दें, आपके पास एक मुद्दा है और इसे हल करने के लिए एक डिज़ाइन दृष्टिकोण की तलाश कर रहे हैं। मेरा सुझाव: विरासत वाली कक्षाओं में बस एक नेस्टेड वर्ग हो सकता है जो सुपर क्लास के बिल्डर वर्ग से विरासत में मिला है, इसलिए विरासत वर्ग में मूल रूप से सुपर क्लास के समान संरचना है और एक बिल्डर पैटर्न है जिसमें अतिरिक्त कार्यों के साथ ठीक कार्य करना चाहिए उप-वर्ग के अतिरिक्त गुणों के लिए ।।


जब यह एक अच्छा विचार है

एक तरफ रेंटिंग, बिल्डर पैटर्न में एक आला है । हम सभी इसे जानते हैं क्योंकि हम सभी ने इस विशेष बिल्डर को एक बिंदु या किसी अन्य पर सीखा है: StringBuilder- यहाँ उद्देश्य सरल निर्माण नहीं है, क्योंकि स्ट्रिंग्स का निर्माण और संक्षिप्त करना आसान नहीं हो सकता है आदि यह एक महान बिल्डर है क्योंकि इसका प्रदर्शन लाभ है ।

प्रदर्शन का लाभ इस प्रकार है: आपके पास वस्तुओं का एक समूह है, वे एक अपरिवर्तनीय प्रकार के हैं, आपको उन्हें एक अपरिवर्तनीय प्रकार की एक वस्तु तक ढहाने की आवश्यकता है। यदि आप इसे वृहद रूप से करते हैं, तो आप यहाँ कई मध्यस्थ वस्तुओं का निर्माण करेंगे, इसलिए यह सब एक साथ करना कहीं अधिक बेहतर और आदर्श है।

इसलिए मुझे लगता है कि जब यह एक अच्छा विचार है, तो इसका मुख्य मुद्दा समस्या डोमेन में है StringBuilder: अपरिवर्तनीय प्रकार के कई उदाहरणों को एक अपरिवर्तनीय प्रकार के एकल उदाहरण में बदलने की आवश्यकता


मुझे नहीं लगता कि आपका दिया गया उदाहरण या तो नियम को संतुष्ट करता है। वहाँ कुछ भी नहीं है मुझे एक अवैध राज्य में एक Cfg बनाने रोक, और जबकि मापदंडों ctor से बाहर ले जाया गया है वे सिर्फ एक कम मुहावरेदार और अधिक क्रिया स्थान पर ले जाया गया है। fooBuilder.withBar(2).withBang("Hello").withBaz(someComplexObject).build()फॉक्‍स के निर्माण के लिए एक सक्‍सेसफुल एपीआई प्रदान करता है और यह बिल्डर में वास्तविक त्रुटि जाँच की पेशकश कर सकता है। बिल्डर के बिना ऑब्जेक्ट को खुद ही अपने इनपुट्स की जांच करनी होगी, जिसका मतलब है कि हम पहले से बेहतर नहीं हैं।
फोशी

डीटीओ अपनी संपत्तियों को सेटर पर, एनोटेशन के साथ कई मायनों में घोषित कर सकते हैं, हालांकि आप इसके बारे में जाना चाहते हैं - सत्यापन एक अलग समस्या है और उनके बिल्डर दृष्टिकोण में वे कंस्ट्रक्शन में सत्यापन को दर्शाते हैं, यह तर्क पूरी तरह से फिट होगा मेरे दृष्टिकोण में। हालाँकि, आम तौर पर डीटीओ का उपयोग करने के लिए इसे मान्य करना बेहतर होगा क्योंकि जैसा कि मैं दिखाता हूं - डीटीओ का उपयोग कई प्रकार के निर्माण के लिए किया जा सकता है और इसलिए इस पर सत्यापन होने से कई प्रकारों को मान्य करने के लिए उधार दिया जाएगा। बिल्डर केवल उसी विशेष प्रकार के लिए मान्य करता है जो उसके लिए बनाया गया है।
जिमी हॉफ

शायद सबसे लचीला तरीका बिल्डर में एक स्थिर सत्यापन कार्य करना होगा, जो एकल Fieldableपैरामीटर को स्वीकार करता है । मैं इस सत्यापन समारोह को ToBeBuiltनिर्माणकर्ता से कहूंगा , लेकिन इसे कहीं से भी, किसी भी चीज से बुलाया जा सकता है। यह एक विशिष्ट कार्यान्वयन के लिए बिना अनावश्यक कोड की क्षमता को समाप्त करता है। (और यदि आप Fieldableअवधारणा को पसंद नहीं करते हैं, तो आपको व्यक्तिगत क्षेत्रों में सत्यापन समारोह में जाने से रोकने के लिए कुछ भी नहीं है - लेकिन अब कम से कम तीन स्थानों पर फ़ील्ड-सूची को बनाए रखना होगा।)
aliteralmind

+1 और ऐसा वर्ग जिसके निर्माणकर्ता में बहुत अधिक निर्भरताएँ हैं, जाहिर है कि यह पर्याप्त रूप से सामंजस्यपूर्ण नहीं है और इसे छोटी कक्षाओं में वापस लाया जाना चाहिए।
बसिलेव

@ जिम्मीहॉफ: आह, मैं देख रहा हूं, आपने इसे छोड़ दिया है। मुझे यकीन नहीं है कि मुझे इस और एक बिल्डर के बीच का अंतर दिखाई दे रहा है, फिर, इसके अलावा कॉल करने के बजाय ctor में एक कॉन्फिग इंस्टेंस पास करता है। कुछ बिल्डर पर .build, और यह कि एक बिल्डर के पास सभी पर शुद्धता की जाँच के लिए अधिक स्पष्ट रास्ता है। आँकड़े। प्रत्येक व्यक्ति चर इसकी मान्य सीमाओं के भीतर हो सकता है, लेकिन उस विशेष क्रमपरिवर्तन में अमान्य है। .build इस की जाँच कर सकता है, लेकिन वस्तु को ctor में पास करने से वस्तु के अंदर त्रुटि की जाँच की आवश्यकता होती है - icky!
फॉशी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.