2008-09-02 29 views
105

मान लीजिए कि आप एक वर्ग, ग्राहक कहा जाता है जो निम्नलिखित क्षेत्रों में शामिल है करते हैं:कितने कन्स्ट्रक्टर तर्क बहुत अधिक हैं?

  • उपयोगकर्ता नाम
  • ईमेल
  • प्रथम नाम
  • अंतिम नाम

के भी के अनुसार मान लें कि आपके व्यापार तर्क, सभी ग्राहक वस्तुओं में इन चार गुणों को परिभाषित किया जाना चाहिए।

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

मैंने कक्षाओं को देखा है जो उनके कन्स्ट्रक्टर में 20+ तर्क लेते हैं और यह उनका उपयोग करने के लिए सिर्फ एक दर्द है। लेकिन, वैकल्पिक रूप से, यदि आपको इन फ़ील्डों की आवश्यकता नहीं है तो आप इन गुणों को निर्दिष्ट करने के लिए कॉलिंग कोड पर भरोसा करते हुए अपरिभाषित जानकारी, या बदतर, ऑब्जेक्ट रेफरेंसिंग त्रुटियों के जोखिम में भाग लेते हैं।

क्या इसके लिए कोई विकल्प हैं या क्या आपको बस यह तय करना है कि आपके पास रहने के लिए एक्स कन्स्ट्रक्टर तर्कों की एक्स कितनी है?

उत्तर

96

दो डिजाइन पर विचार करने के दृष्टिकोण

essence पैटर्न

fluent interface पैटर्न

इन दोनों के इरादे में समान हैं, कि हम धीरे-धीरे एक मध्यवर्ती वस्तु का निर्माण, और उसके बाद हमारा लक्ष्य वस्तु बनाने एक ही चरण में

कार्रवाई में धाराप्रवाह इंटरफ़ेस का एक उदाहरण होगा:

public class CustomerBuilder { 
    String surname; 
    String firstName; 
    String ssn; 
    public static CustomerBuilder customer() { 
     return new CustomerBuilder(); 
    } 
    public CustomerBuilder withSurname(String surname) { 
     this.surname = surname; 
     return this; 
    } 
    public CustomerBuilder withFirstName(String firstName) { 
     this.firstName = firstName; 
     return this; 
    } 
    public CustomerBuilder withSsn(String ssn) { 
     this.ssn = ssn; 
     return this; 
    } 
    // client doesn't get to instantiate Customer directly 
    public Customer build() { 
     return new Customer(this);    
    } 
} 

public class Customer { 
    private final String firstName; 
    private final String surname; 
    private final String ssn; 

    Customer(CustomerBuilder builder) { 
     if (builder.firstName == null) throw new NullPointerException("firstName"); 
     if (builder.surname == null) throw new NullPointerException("surname"); 
     if (builder.ssn == null) throw new NullPointerException("ssn"); 
     this.firstName = builder.firstName; 
     this.surname = builder.surname; 
     this.ssn = builder.ssn; 
    } 

    public String getFirstName() { return firstName; } 
    public String getSurname() { return surname; } 
    public String getSsn() { return ssn; }  
} 


import static com.acme.CustomerBuilder.customer; 

public class Client { 
    public void doSomething() { 
     Customer customer = customer() 
      .withSurname("Smith") 
      .withFirstName("Fred") 
      .withSsn("123XS1") 
      .build(); 
    } 
} 
+2

मुझे इसे "नामित पैरामीटर इडियॉम" के रूप में जाना जाता है: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.18। संबंधित: वहाँ भी "नामांकित निर्माता मुहावरा" है: http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8 – Frank

+0

आप कोड के फोन करने वाले और कॉल प्राप्त करने वाला क्षेत्रों को अलग करने के लिए कर सकते हैं यह स्पष्ट है कि वे अलग-अलग संस्थाएं हैं? –

+0

शून्य मानों के लिए चेक (उपनाम == नल || firstName == null || ssn == null) ग्राहक ऑब्जेक्ट पर किया जाना चाहिए। ग्राहक ऑब्जेक्ट बनने से पहले इन मानों को संशोधित करने की संभावना के कारण यह है। इसके अलावा, सुनिश्चित करें कि ग्राहक वर्ग के क्षेत्र सभी को "निजी अंतिम" घोषित किया गया है - जिससे ग्राहक अपरिवर्तनीय हो। – drozzy

0

जब तक कि यह 1 से अधिक तर्क नहीं है, मैं हमेशा सरणी या ऑब्जेक्ट्स को कन्स्ट्रक्टर पैरामीटर के रूप में उपयोग करता हूं और यह सुनिश्चित करने के लिए त्रुटि जांच पर भरोसा करता हूं कि आवश्यक पैरामीटर हैं।

3

यदि आपके पास अनजाने में कई तर्क हैं, तो उन्हें केवल structs/POD कक्षाओं में पैकेज करें, जिसे आप जिस कक्षा का निर्माण कर रहे हैं उसके आंतरिक कक्षाओं के रूप में घोषित किया जाता है। इस तरह से कोड बनाने के दौरान आपको अभी भी फ़ील्ड की आवश्यकता हो सकती है जो कन्स्ट्रक्टर को उचित रूप से पठनीय कहलाता है।

0

बस डिफ़ॉल्ट तर्कों का उपयोग करें। एक भाषा (उदाहरण के लिए, पीएचपी) डिफ़ॉल्ट विधि तर्क का समर्थन करता है में, आप विधि हस्ताक्षर में ऐसा कर सकता है:

public function doSomethingWith($this = val1, $this = val2, $this = val3)

वहाँ अन्य उस विधि से अधिक भार का समर्थन ऐसे भाषाओं में के रूप में डिफ़ॉल्ट मान बनाने के तरीके, कर रहे हैं ।

बेशक, यदि आप फ़ील्ड घोषित करते हैं तो आप डिफ़ॉल्ट मान भी सेट कर सकते हैं, अगर आपको ऐसा करने के लिए उचित लगता है।

यह वास्तव में नीचे आता है कि यह आपके लिए डिफ़ॉल्ट मान सेट करने के लिए उचित है या नहीं, या यदि आपकी वस्तुओं को हर समय निर्माण पर specced किया जाना चाहिए। यह वास्तव में एक निर्णय है कि केवल आप ही कर सकते हैं।

2

स्टाइल बहुत मायने रखता है, और ऐसा लगता है कि यदि 20+ तर्कों वाला कोई कन्स्ट्रक्टर है, तो डिज़ाइन को बदला जाना चाहिए। उचित डिफ़ॉल्ट प्रदान करें।

2

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

2

मुझे लगता है कि प्रत्येक मूल्य के लिए स्वीकार्य डिफ़ॉल्ट खोजने का सबसे आसान तरीका होगा।इस मामले में, प्रत्येक फ़ील्ड ऐसा लगता है कि इसे बनाने की आवश्यकता होगी, इसलिए फ़ंक्शन कॉल को संभवतः अधिभारित करें ताकि अगर कॉल में कुछ परिभाषित नहीं किया गया है, तो इसे डिफ़ॉल्ट पर सेट करने के लिए।

फिर, ताकि मूलभूत मूल्यों बदला जा सकता है प्रत्येक प्रॉपर्टी के लिए गेटर और सेटर कार्यों बनाते हैं।

जावा कार्यान्वयन:

public static void setEmail(String newEmail){ 
    this.email = newEmail; 
} 

public static String getEmail(){ 
    return this.email; 
} 

यह भी अपने वैश्विक चर सुरक्षित रखने के लिए अच्छा अभ्यास है।

3

स्टीव Mcconnell कोड में लिखते पूरा मुसीबत एक समय में उनके सिर में और अधिक 7 बातें रखते हुए, ताकि संख्या मैं नीचे रहने का प्रयास होगा है कि लोगों को।

+0

लेकिन Weinschenk देखते हैं, * 100 चीजें हर डिजाइनर लोग बारे में पता करने के लिए की जरूरत है *, 48. जाहिर है इस खारिज कर दिया गया है चार एक और अधिक सटीक ऊपरी सीमा है। –

0

मैं 7 आइटम सीमा Boojiboy का उल्लेख है पर सहमत हैं। इसके अलावा, यह किसी अन्य डेटा स्रोत के लिए प्राथमिक कुंजी के माध्यम से अज्ञात (या विशेष) प्रकार, IDictionary, या संकेतक को देखने लायक हो सकता है।

4

मुझे लगता है कि "शुद्ध OOP" जवाब अगर वर्ग पर कार्रवाई अमान्य हैं जब कुछ सदस्यों आरंभ नहीं कर रहे हैं, तो इन सदस्यों निर्माता द्वारा निर्धारित किया जाना चाहिए कि है। हमेशा ऐसा मामला होता है जहां डिफ़ॉल्ट मानों का उपयोग किया जा सकता है, लेकिन मुझे लगता है कि हम उस मामले पर विचार नहीं कर रहे हैं। एपीआई तय होने पर यह एक अच्छा तरीका है, क्योंकि एपीआई सार्वजनिक होने के बाद एकल स्वीकार्य कन्स्ट्रक्टर को बदलना आपके और आपके कोड के सभी उपयोगकर्ताओं के लिए एक दुःस्वप्न होगा।

सी # में, मैं क्या डिजाइन दिशा निर्देशों के बारे समझते हैं कि यह जरूरी एक ही रास्ता स्थिति से निपटने के नहीं है। विशेष रूप से WPF वस्तुओं के साथ, आप जो .NET कक्षाएं parameterless कंस्ट्रक्टर्स के पक्ष में जाते हैं और अगर डेटा विधि कॉल करने से पहले एक वांछनीय राज्य के लिए शुरू नहीं किया गया है अपवाद फेंक जाएगा मिल जाएगा। यह शायद मुख्य रूप से घटक-आधारित डिजाइन के लिए विशिष्ट है; मैं एक .NET कक्षा के ठोस उदाहरण के साथ नहीं आ सकता जो इस तरह से व्यवहार करता है। आपके मामले में, यह निश्चित रूप से यह सुनिश्चित करने के लिए परीक्षण पर बढ़े हुए बोझ का कारण बनता है कि वर्ग को डेटा स्टोर में कभी भी सहेजा नहीं जाता है जब तक कि गुणों को सत्यापित नहीं किया जाता है। ईमानदारी से इस वजह से मैं "कन्स्ट्रक्टर आवश्यक गुण सेट करता हूं" दृष्टिकोण पसंद करूंगा यदि आपका एपीआई या तो पत्थर में सेट है या सार्वजनिक नहीं है।

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

5

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

+0

कभी-कभी यह संभव नहीं है। एक एक्सेल फ़ाइल पर 50 कॉलम के साथ विचार करें जिसे संसाधित करने की आवश्यकता है। 50 तर्कों के साथ एक कन्स्ट्रक्टर रखने वाले MyExcelFileLine क्लास का विचार काफी डरावना है। –

13

आपके मामले में, निर्माता के साथ चिपके रहते हैं। जानकारी ग्राहक में है और 4 फ़ील्ड ठीक हैं।

यदि आपके पास कई आवश्यक और वैकल्पिक फ़ील्ड हैं तो कन्स्ट्रक्टर सबसे अच्छा समाधान नहीं है। जैसा कि @boojiboy ने कहा, पढ़ना मुश्किल है और क्लाइंट कोड लिखना भी मुश्किल है।

@contagious वैकल्पिक गुणों के लिए डिफ़ॉल्ट पैटर्न और सेटर्स का उपयोग करने का सुझाव दिया। यह जरूरी है कि खेतों में उत्परिवर्तनीय हो, लेकिन यह मामूली समस्या है।

प्रभावशाली जावा 2 पर जोशुआ ब्लॉक का कहना है कि इस मामले में आपको एक निर्माता पर विचार करना चाहिए। एक उदाहरण पुस्तक से लिया:

public class NutritionFacts { 
    private final int servingSize; 
    private final int servings; 
    private final int calories; 
    private final int fat; 
    private final int sodium; 
    private final int carbohydrate; 

    public static class Builder { 
    // required parameters 
    private final int servingSize; 
    private final int servings; 

    // optional parameters 
    private int calories   = 0; 
    private int fat    = 0; 
    private int carbohydrate  = 0; 
    private int sodium   = 0; 

    public Builder(int servingSize, int servings) { 
     this.servingSize = servingSize; 
     this.servings = servings; 
    } 

    public Builder calories(int val) 
     { calories = val;  return this; } 
    public Builder fat(int val) 
     { fat = val;   return this; } 
    public Builder carbohydrate(int val) 
     { carbohydrate = val; return this; } 
    public Builder sodium(int val) 
     { sodium = val;   return this; } 

    public NutritionFacts build() { 
     return new NutritionFacts(this); 
    } 
    } 

    private NutritionFacts(Builder builder) { 
    servingSize  = builder.servingSize; 
    servings   = builder.servings; 
    calories   = builder.calories; 
    fat    = builder.fat; 
    soduim   = builder.sodium; 
    carbohydrate  = builder.carbohydrate; 
    } 
} 

और फिर इस तरह इसका इस्तेमाल:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). 
     calories(100).sodium(35).carbohydrate(27).build(); 

उपरोक्त उदाहरण से Effective Java 2

लिया गया था और वह न केवल निर्माता के लिए लागू होता है। Implementation Patterns में केंट बैक हवाला देते हुए:

setOuterBounds(x, y, width, height); 
setInnerBounds(x + 2, y + 2, width - 4, height - 4); 

आयत एक वस्तु के रूप स्पष्ट बनाना कोड बेहतर बताते हैं:

setOuterBounds(bounds); 
setInnerBounds(bounds.expand(-2)); 
+5

बेशक यदि सभी तर्क कन्स्ट्रक्टर में आवश्यक हैं, तो आप केवल एक विशाल कन्स्ट्रक्टर को एक स्थान से दूसरे स्थान पर ले जाना समाप्त कर देते हैं। – drozzy

1

मैं अपने स्वयं के निर्माण/सत्यापन के साथ अपनी खुद की एक वस्तु में समान क्षेत्रों संक्षेप था तर्क।

उदाहरण के लिए

कहो, अगर तुम मिल गया है

  • व्यवसायफ़ोन
  • BusinessAddress
  • Homephone
  • HomeAddress

मैं एक वर्ग है कि फोन संग्रहीत करता है बनाने के लिए और एक साथ संबोधित करता हूँ एक टैग के साथ एक "घर" या एक "व्यापार" फोन/पता गीला निर्दिष्ट। और फिर 4 फ़ील्ड को केवल एक सरणी में कम करें।

ContactInfo cinfos = new ContactInfo[] { 
    new ContactInfo("home", "+123456789", "123 ABC Avenue"), 
    new ContactInfo("biz", "+987654321", "789 ZYX Avenue") 
}; 

Customer c = new Customer("john", "doe", cinfos); 

कि यह स्पेगेटी की तरह कम लग रही है बनाना चाहिए।

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

और निम्नलिखित भी है संभव समाधान:

  • बजाय एक ही वर्ग में भंडारण के सत्यापन तर्क से बाहर फैला। मान्य करें जब उपयोगकर्ता इनपुट उन्हें और फिर डेटाबेस परत आदि पर फिर से मान्य ...
  • एक CustomerFactory वर्ग है कि मुझे का निर्माण Customer रों
  • @ मार्शियो समाधान भी दिलचस्प है ...
21

मैं मदद मिलेगी बनाओ देखें कि कुछ लोग सात की ऊपरी सीमा के रूप में सिफारिश कर रहे हैं। जाहिर है, यह सच नहीं है कि लोग एक बार में अपने सिर में सात चीजें रख सकते हैं; वे केवल चार याद कर सकते हैं (सुसान Weinschenk, 100 चीजें प्रत्येक डिजाइनर को लोगों के बारे में जानना चाहिए, 48)।फिर भी, मैं चार को एक उच्च पृथ्वी कक्षा के रूप में मानता हूं। लेकिन ऐसा इसलिए है क्योंकि मेरी सोच बॉब मार्टिन द्वारा बदल दी गई है।

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

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

वह पठनीयता के कारण इस कहते हैं; लेकिन टेस्टेबिलिटी के कारण भी:

सभी परीक्षण मामलों को लिखने में कठिनाई की कल्पना करें ताकि यह सुनिश्चित किया जा सके कि तर्क के सभी संयोजन संयोजन सही तरीके से काम करते हैं।

मैं आपको अपनी पुस्तक की एक प्रति ढूंढने और फ़ंक्शन तर्क (40-43) की पूरी चर्चा पढ़ने के लिए प्रोत्साहित करता हूं।

मैं उन लोगों से सहमत हूं जिन्होंने एकल उत्तरदायित्व सिद्धांत का उल्लेख किया है। मेरे लिए यह विश्वास करना मुश्किल है कि एक वर्ग जिसे दो या तीन मूल्यों/वस्तुओं को उचित चूक के बिना चाहिए, वास्तव में केवल एक ही जिम्मेदारी है, और निकाली गई किसी अन्य वर्ग के साथ बेहतर नहीं होगी।

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

हालांकि, कंप्यूटर विज्ञान में सभी चीजों के साथ के रूप में, वहाँ निस्संदेह निर्माता मानकों की एक बड़ी संख्या होने के लिए मान्य मामले हैं। बड़ी संख्या में पैरामीटर का उपयोग करने से बचने के लिए अपने कोड को मतभेद न करें; लेकिन यदि आप बड़ी संख्या में पैरामीटर का उपयोग करते हैं, तो इसे रोकें और इसे कुछ विचार दें, क्योंकि इसका मतलब यह हो सकता है कि आपका कोड पहले से ही अलग हो गया है।

+0

मैं कभी भी रचनाकारों को तर्क नहीं देता ... मैं उन्हें सभी को एक इनिट फ़ंक्शन में पास करता हूं, और तर्क 1 ऑब्जेक्ट होता है जिसमें सभी आवश्यक तर्क होते हैं। लेकिन फिर, मैं जावास्क्रिप्ट करता हूं ... जावा क्या है? – andygoestohollywood

+1

मैंने हमेशा सोचा है, यह "डेटा क्लास" के साथ कैसे खेलता है, जो केवल संबंधित डेटा रखने के लिए मौजूद है। यदि आप इसे ओपी के प्रश्न पर लागू करते हैं, तो उसकी कक्षा सिर्फ ग्राहक के लिए डेटा रखती है। इस मामले में पैरामीटर कैसे कम किए जा सकते हैं इस पर कोई विचार? – 0cd

+0

@ पुनीत, एक समान आलोचना भी है जहां एक निर्माता केवल 3 तर्क ले सकता है, लेकिन उन सभी तर्कों में बड़ी परिसर कक्षाएं हैं। तो संक्षेप में आप कन्स्ट्रक्टर को 60 पैरामीटर भेज रहे हैं, यह सिर्फ इतना है कि वे पैक किए गए हैं। – LegendLength