एक विधि को रिफलेक्टर करने का सबसे अच्छा तरीका क्या है जिसमें बहुत सारे (6+) पैरामीटर हैं?


102

कभी-कभी मैं मापदंडों की एक असहज संख्या के साथ तरीकों में आता हूं। अधिक बार नहीं, वे निर्माता लगते हैं। ऐसा लगता है कि वहाँ एक बेहतर तरीका होना चाहिए, लेकिन मैं नहीं देख सकता कि यह क्या है।

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

मैंने मापदंडों की सूची का प्रतिनिधित्व करने के लिए संरचना का उपयोग करने के बारे में सोचा है, लेकिन यह समस्या को एक स्थान से दूसरे स्थान पर स्थानांतरित करने के लिए लगता है, और प्रक्रिया में एक और प्रकार बनाता है।

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

तो यह एक सुधार की तरह प्रतीत नहीं होता है। तो सबसे अच्छा तरीका क्या है?


आपने कहा "संरचना"। उस शब्द की विभिन्न प्रोग्रामिंग भाषाओं में भिन्न अर्थ हैं। इसका मतलब क्या है?
जय बज़ुजी १३'०

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

इस लेख में विषय पर कुछ अच्छी जानकारी है। जावास्क्रिप्ट विशिष्ट, लेकिन सिद्धांतों को अन्य भाषाओं में फिर से लागू किया जा सकता है।
लाला

जवाबों:


94

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

उदाहरण के लिए, यदि आप किसी आयत के लिए विनिर्देश पास कर रहे हैं, तो आप x, y, चौड़ाई और ऊँचाई पास कर सकते हैं या आप बस एक आयत वस्तु पास कर सकते हैं जिसमें x, y, चौड़ाई और ऊँचाई हो।

इस तरह की चीजों के लिए देखो जब इसे साफ करने के लिए कुछ को फिर से भरना। यदि तर्कों को वास्तव में संयोजित नहीं किया जा सकता है, तो यह देखना शुरू करें कि क्या आपके पास एकल जिम्मेदारी सिद्धांत का उल्लंघन है।


4
अच्छा विचार लेकिन बुरा उदाहरण; आयत के लिए निर्माता के पास 4 तर्क होंगे। यदि विधि आयत निर्देशांक / आयामों के 2 सेट की उम्मीद कर रही थी तो यह अधिक समझ में आता है। तब आप X1, x2, y1, y2 के बजाय 2 आयतें पास कर सकते थे ...
Outlaw Programmer

2
काफी उचित। जैसा कि मैंने कहा, यह वास्तव में केवल करने के लिए समझ में आता है अगर आप कई तार्किक समूहों के साथ समाप्त होते हैं।
मैथ्यू ब्रूकर

23
+1: सिंगल रिस्पॉन्सिबिलिटी के लिए, इसके सभी उत्तरों में कुछ टिप्पणियों में से एक जो वास्तव में सच्चे मुद्दे को संबोधित कर रही है। अपनी पहचान बनाने के लिए वास्तव में 7 स्वतंत्र मूल्यों की क्या आवश्यकता है।
एंथनीवजोन

6
@AnthonyWJones मैं असहमत हूं। वर्तमान मौसम की स्थिति के लिए डेटा की पहचान बनाने के लिए कई और स्वतंत्र मूल्य हो सकते हैं।
फंक्शनल 7

107

मैं आपको C # मतलब करने जा रहा हूं । इनमें से कुछ चीजें अन्य भाषाओं पर भी लागू होती हैं।

आपके पास कई विकल्प हैं:

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

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

हालाँकि, आप अपरिवर्तनीयता खो देते हैं, और आप यह सुनिश्चित करने की क्षमता खो देते हैं कि संकलन समय पर ऑब्जेक्ट का उपयोग करने से पहले आवश्यक मान सेट किए गए हैं।

बिल्डर पैटर्न

stringऔर के बीच संबंध के बारे में सोचो StringBuilder। आप इसे अपनी कक्षाओं के लिए प्राप्त कर सकते हैं। मैं इसे एक नेस्टेड क्लास के रूप में लागू करना पसंद करता हूं, इसलिए क्लास Cमें संबंधित वर्ग है C.Builder। मुझे बिल्डर पर एक धाराप्रवाह इंटरफ़ेस भी पसंद है। ठीक है, आप इस तरह वाक्यविन्यास प्राप्त कर सकते हैं:

C c = new C.Builder()
    .SetX(4)    // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();     // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

मेरे पास एक PowerShell स्क्रिप्ट है जो मुझे यह सब करने के लिए बिल्डर कोड उत्पन्न करने देता है, जहां इनपुट जैसा दिखता है:

class C {
    field I X
    field string Y
}

इसलिए मैं संकलन समय पर उत्पन्न कर सकता हूं। partialकक्षाएं मुझे उत्पन्न कोड को संशोधित किए बिना मुख्य वर्ग और बिल्डर दोनों का विस्तार करने देती हैं।

"इंट्रोड्यूस पर्मेट ऑब्जेक्ट" रिफैक्टिंगरिफैक्टिंग कैटलॉग देखें । विचार यह है कि आप अपने द्वारा पास किए गए कुछ मापदंडों को लेते हैं और उन्हें एक नए प्रकार में डालते हैं, और फिर इसके बजाय उस प्रकार का एक उदाहरण देते हैं। यदि आप ऐसा बिना सोचे-समझे करेंगे, तो आप वापस वहीं आ जाएंगे जहाँ आपने शुरुआत की थी:

new C(a, b, c, d);

हो जाता है

new C(new D(a, b, c, d));

हालांकि, इस दृष्टिकोण में आपके कोड पर सकारात्मक प्रभाव डालने की सबसे बड़ी क्षमता है। तो, इन चरणों का पालन करके जारी रखें:

  1. मापदंडों के सबसेट के लिए देखो जो एक साथ समझ में आता है। बिना किसी फ़ंक्शन के सभी मापदंडों को समूहबद्ध करने के लिए बस आपको बहुत कुछ नहीं मिलता है; लक्ष्य के लिए समूहीकरण है जो समझ में आता है। आपको पता चल जाएगा कि नए प्रकार का नाम स्पष्ट है या नहीं।

  2. अन्य स्थानों के लिए देखें जहां ये मान एक साथ उपयोग किए जाते हैं, और वहां नए प्रकार का भी उपयोग करते हैं। संभावना है, जब आपने मूल्यों के एक सेट के लिए एक अच्छा नया प्रकार पाया है जो आप पहले से ही सभी जगह उपयोग करते हैं, तो वह नया प्रकार उन सभी स्थानों में भी समझ में आएगा।

  3. कार्यक्षमता के लिए देखें जो मौजूदा कोड में है, लेकिन नए प्रकार पर है।

उदाहरण के लिए, हो सकता है कि आपको कुछ कोड दिखाई दें जो इस प्रकार हैं:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

आप पैरामीटर ले सकते हैं minSpeedऔर maxSpeedउन्हें एक नए प्रकार में डाल सकते हैं:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

यह बेहतर है, लेकिन वास्तव में नए प्रकार का लाभ उठाने के लिए, नए प्रकार में तुलना को स्थानांतरित करें:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

और अब हम कहीं जा रहे हैं: SpeedIsAcceptable()अब का कार्यान्वयन कहता है कि आपका क्या मतलब है, और आपके पास एक उपयोगी, पुन: प्रयोज्य वर्ग है। (अगले स्पष्ट कदम SpeedRangeमें करना है Range<Speed>।)

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


4
मेरा सुझाव है कि पहले "परिचय वस्तु का परिचय दें", और अन्य विकल्पों के लिए केवल तभी वापसी करें जब आप एक अच्छा पैरामीटर ऑब्जेक्ट नहीं बना सकते।
डगलस लीडर

4
शानदार जवाब। यदि आपने c # सिंथैटिक शर्करा से पहले रीफैक्टरिंग स्पष्टीकरण का उल्लेख किया है, तो इसे उच्च IMHO वोट दिया जाएगा।
rpattabi

10
ऊह! +1 के लिए "आपको पता चल जाएगा कि नए प्रकार का नाम स्पष्ट है या नहीं।"
सीन मैकमिलन

20

यदि यह एक निर्माणकर्ता है, खासकर यदि कई अतिभारित संस्करण हैं, तो आपको बिल्डर पैटर्न को देखना चाहिए:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

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


बहुत बढ़िया जवाब। शायद "एक वर्ग में मापदंडों को डाल" से भी अधिक प्रासंगिक है जो उत्तर देता है कि सभी (मेरे सहित) ने दिया।
राउटर लीवेन्स

1
यह शायद एक बुरा विचार है कि आपकी कक्षा को केवल निर्माणकर्ता के लिए बहुत सारे मापदंडों में पारित होने से बचने के लिए परिवर्तनशील बनाया जा सकता है।
प्रोग्रामर

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

जब आप कॉन्फ़िगरेशन को बाहरी बना सकते हैं, तो कई मामलों में यह अनावश्यक है, खासकर अगर यह प्रोग्राम स्टेट द्वारा संचालित है या किसी दिए गए प्रोग्राम के लिए मानक है (XML XML पर विचार करें, जो नाम-पता हो सकता है, विभिन्न उपकरणों के साथ मान्य हो सकता है, और c)।
kdgregory

मुझे बिल्डर पैटर्न पसंद है, लेकिन मैं अपने अपरिवर्तनीय और उत्परिवर्तनीय बिल्डर प्रकारों को अलग करता हूं, जैसे कि स्ट्रिंग / स्ट्रिंगबर्ल, लेकिन मैं नेस्टेड वर्गों का उपयोग करता हूं: फू / फू.बुइटल। मेरे पास सरल डेटा कक्षाओं के लिए ऐसा करने के लिए कोड उत्पन्न करने के लिए एक PowerShell स्क्रिप्ट है।
जे बाज़ुज़ी

10

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

उदाहरण के बजाय:

driver.connect(host, user, pass)

आप उपयोग कर सकते हैं

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV


5
मैं निश्चित रूप से कोड का पहला टुकड़ा अधिक पसंद करूंगा। मैं सहमत हूं, कि एक निश्चित सीमा है, जिसके ऊपर सुन्न रफ पैरामीटर बदसूरत हो जाते हैं, लेकिन मेरे स्वाद के लिए, 3 स्वीकार्य होगा।
ब्लाबला 999

10

यह फाउलर और बेक की किताब से उद्धृत किया गया है: "रिफैक्टिंग"

लंबी पैरामीटर सूची

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


7

मैं एक बुद्धिमान-दरार की तरह आवाज़ नहीं करना चाहता, लेकिन आपको यह सुनिश्चित करने के लिए भी जांचना चाहिए कि आप जिस डेटा को पास कर रहे हैं वह वास्तव में पास होना चाहिए: एक निर्माणकर्ता (या उस मामले के लिए विधि) के लिए सामान पास करना एक तरह से बदबू आ रही है। व्यवहार पर थोड़ा जोर किसी वस्तु ।

मुझे गलत मत करो: तरीके और कंस्ट्रक्टर जाएगा मानकों का एक बहुत कभी कभी की है। लेकिन जब सामना किया, व्यवहार के साथ डेटा encapsulating पर विचार करने की कोशिश करो इसके बजाय ।

इस तरह की गंध (क्योंकि हम रिफैक्टिंग के बारे में बात कर रहे हैं, यह भयानक शब्द उचित लगता है ...) उन वस्तुओं के लिए भी पता लगाया जा सकता है जिनके पास बहुत कुछ है (पढ़ें: कोई भी) गुण या गेटर्स / सेटर।


7

जब मैं लंबी पैरामीटर सूची देखता हूं, तो मेरा पहला सवाल यह है कि क्या यह फ़ंक्शन या ऑब्जेक्ट बहुत अधिक कर रहा है। विचार करें:

EverythingInTheWorld earth=new EverythingInTheWorld(firstCustomerId,
  lastCustomerId,
  orderNumber, productCode, lastFileUpdateDate,
  employeeOfTheMonthWinnerForLastMarch,
  yearMyHometownWasIncorporated, greatGrandmothersBloodType,
  planetName, planetSize, percentWater, ... etc ...);

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

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

 Order order=new Order(customerName, customerAddress, customerCity,
   customerState, customerZip,
   orderNumber, orderType, orderDate, deliveryDate);

हम कर सकते थे:

Customer customer=new Customer(customerName, customerAddress,
  customerCity, customerState, customerZip);
Order order=new Order(customer, orderNumber, orderType, orderDate, deliveryDate);

बेशक, मैं उन कार्यों को पसंद करता हूं जो सिर्फ 1 या 2 या 3 पैरामीटर लेते हैं, कभी-कभी हमें यह स्वीकार करना पड़ता है कि, वास्तविक रूप से, यह फ़ंक्शन एक गुच्छा लेता है, और स्वयं की संख्या वास्तव में जटिलता पैदा नहीं करती है। उदाहरण के लिए:

Employee employee=new Employee(employeeId, firstName, lastName,
  socialSecurityNumber,
  address, city, state, zip);

हाँ, यह खेतों का एक समूह है, लेकिन शायद हम सभी उनके साथ क्या करने जा रहे हैं, उन्हें डेटाबेस रिकॉर्ड में सहेजना है या उन्हें स्क्रीन पर फेंकना है या कुछ ऐसे। यहां वास्तव में बहुत अधिक प्रसंस्करण नहीं है।

जब मेरी पैरामीटर सूचियां लंबी हो जाती हैं, तो मैं बहुत पसंद करता हूं कि क्या मैं खेतों को अलग-अलग प्रकार के डेटा दे सकता हूं। जैसे जब मैं कोई फ़ंक्शन देखता हूं जैसे:

void updateCustomer(String type, String status,
  int lastOrderNumber, int pastDue, int deliveryCode, int birthYear,
  int addressCode,
  boolean newCustomer, boolean taxExempt, boolean creditWatch,
  boolean foo, boolean bar);

और फिर मैं इसके साथ बुलाया देखें:

updateCustomer("A", "M", 42, 3, 1492, 1969, -7, true, false, false, true, false);

मैं चिंतित हूं। कॉल को देखते हुए, यह बिल्कुल स्पष्ट नहीं है कि ये सभी गुप्त नंबर, कोड और झंडे का क्या मतलब है। यह सिर्फ त्रुटियों के लिए पूछ रहा है। एक प्रोग्रामर आसानी से मापदंडों के क्रम के बारे में भ्रमित हो सकता है और गलती से दो स्विच कर सकता है, और यदि वे समान डेटा प्रकार हैं, तो कंपाइलर इसे स्वीकार करेगा। मेरे पास बहुत अधिक हस्ताक्षर हैं जहां ये सभी चीजें हैं, तो एक कॉल "A" के बजाय Type.ACTIVE जैसी चीजों में गुजरती है और "झूठी" के बजाय CreditWatch.NO, आदि।


5

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

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

इसका विवरण प्रभावी जावा, 2 डी एड।, पी में वर्णित है। 11. विधि तर्कों के लिए, एक ही पुस्तक (पृष्ठ 189) पैरामीटर सूचियों को छोटा करने के लिए तीन दृष्टिकोणों का वर्णन करती है:

  • कम तर्क लेने वाले कई तरीकों में विधि को तोड़ें
  • मापदंडों के समूहों का प्रतिनिधित्व करने के लिए स्थिर सहायक सदस्य वर्ग बनाएं, यानी के DinoDonkeyबजाय पास करें dinoऔरdonkey
  • यदि पैरामीटर वैकल्पिक हैं, तो ऊपर दिए गए बिल्डर को तरीकों के लिए अपनाया जा सकता है, सभी मापदंडों के लिए एक ऑब्जेक्ट को परिभाषित करना, आवश्यक लोगों को सेट करना और फिर उस पर कुछ निष्पादन विधि को कॉल करना।

4

मैं डिफॉल्ट कंस्ट्रक्टर और प्रॉपर्टी सेटलर्स का उपयोग करूंगा। C # 3.0 में यह स्वचालित रूप से करने के लिए कुछ अच्छे वाक्यविन्यास हैं।

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

कोड सुधार कंस्ट्रक्टर को सरल बनाने और विभिन्न संयोजनों का समर्थन करने के लिए कई तरीकों का समर्थन नहीं करने के लिए आता है। "कॉलिंग" सिंटैक्स अभी भी थोड़ा "वर्डी" है, लेकिन वास्तव में संपत्ति के निपटारे को मैन्युअल रूप से कॉल करने की तुलना में कोई भी बदतर नहीं है।


2
यह एक वस्तु को नया शनीज़ () मौजूद होने की अनुमति देगा। एक अच्छा OO कार्यान्वयन मौजूदा अपूर्ण स्थिति की वस्तुओं की संभावना को कम करना चाहता है।
एंथनीवजोन

सामान्य तौर पर, देशी हैश / शब्दकोश वाक्य-विन्यास के साथ कोई भी भाषा नामांकित मापदंडों के लिए पर्याप्त विकल्प के साथ आती है (जो कि महान हैं और अक्सर ये परिस्थितियां क्या कहती हैं, लेकिन किसी कारणवश उनका समर्थन करने वाली एकमात्र लोकप्रिय भाषा ग्रह पर सबसे खराब है) ।
अराजक

4

आपने एक अच्छे उत्तर को वारंट करने के लिए पर्याप्त जानकारी नहीं दी है। एक लंबी पैरामीटर सूची स्वाभाविक रूप से खराब नहीं है।

शनीज़ (फू, बार, बाज, क्क्स, फ़्रेड, विल्मा, बार्नी, डिनो, गधा)

के रूप में व्याख्या की जा सकती है:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

इस मामले में आप मापदंडों को एनकैप्सुलेट करने के लिए एक वर्ग बनाने के लिए बहुत बेहतर हैं क्योंकि आप अलग-अलग मापदंडों को इस तरह से अर्थ देते हैं कि कंपाइलर चेक कर सकते हैं और साथ ही साथ कोड को आसानी से पढ़ सकते हैं। इससे बाद में पढ़ना और रिफ्लेक्टर करना भी आसान हो जाता है।

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

वैकल्पिक रूप से यदि आपके पास था:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

यह एक बहुत अलग मामला है क्योंकि सभी वस्तुएं अलग हैं (और उनके ख़राब होने की संभावना नहीं है)। सहमत हैं कि यदि सभी ऑब्जेक्ट आवश्यक हैं, और वे सभी अलग-अलग हैं, तो पैरामीटर क्लास बनाने के लिए बहुत कम समझ में आता है।

इसके अतिरिक्त, कुछ पैरामीटर वैकल्पिक हैं? क्या कोई विधि ओवरराइड है (एक ही विधि का नाम, लेकिन अलग-अलग विधि हस्ताक्षर?) इस प्रकार के विवरण सभी के लिए सबसे अच्छे उत्तर हैं।

* एक संपत्ति बैग के रूप में अच्छी तरह से उपयोगी हो सकता है, लेकिन विशेष रूप से बेहतर नहीं दिया गया है कि कोई पृष्ठभूमि नहीं दी गई है।

जैसा कि आप देख सकते हैं, इस प्रश्न का 1 से अधिक सही उत्तर है। अपना चयन ले लो।


3

आप अपने पैरामीटर को कई गुना सार्थक संरचना / वर्ग (यदि संभव हो तो) में समूहित करने का प्रयास कर सकते हैं।


2

मैं आमतौर पर संरचना के दृष्टिकोण की ओर झुक जाता हूं - संभवतः इन मापदंडों में से अधिकांश किसी तरह से संबंधित हैं और कुछ तत्व की स्थिति का प्रतिनिधित्व करते हैं जो आपकी विधि के लिए प्रासंगिक है।

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


2

आप स्रोत कोड लाइनों के लिए जटिलता का व्यापार कर सकते हैं। यदि विधि स्वयं बहुत अधिक करती है (स्विस चाकू) एक और विधि बनाकर अपने कार्यों को आधा करने की कोशिश करता है। यदि विधि सरल है, तो इसे बहुत अधिक मापदंडों की आवश्यकता है, तो तथाकथित पैरामीटर ऑब्जेक्ट्स जाने का रास्ता है।


2

यदि आपकी भाषा इसका समर्थन करती है, तो नामित मापदंडों का उपयोग करें और यथासंभव संभव (उचित चूक के साथ) वैकल्पिक करें।


1

मुझे लगता है कि आपके द्वारा वर्णित विधि जाने का तरीका है। जब मुझे बहुत सारे मापदंडों और / या एक ऐसी विधि मिल जाती है जिसकी भविष्य में अधिक आवश्यकता होती है, तो मैं आमतौर पर आप के माध्यम से गुजरने के लिए एक ShnizParams ऑब्जेक्ट बनाता हूं, जैसे आप वर्णन करते हैं।


1

कैसे के बारे में यह सब निर्माण में एक बार में स्थापित नहीं है, लेकिन यह गुण / बसने के माध्यम से कर रहा है ? मैंने कुछ .NET क्लासेस देखी हैं जो इस दृष्टिकोण का उपयोग करती हैं जैसे कि Processक्लास:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();

3
C # 3 में वास्तव में इसे आसानी से करने के लिए एक सिंटैक्स है: ऑब्जेक्ट इनिशियलाइज़र।
डैरन थॉमस

1

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


1

यदि आपके पास कई पैरामीटर हैं, तो संभावना है कि विधि बहुत अधिक कर रही है, इसलिए इस विधि को पहले कई छोटे तरीकों में विभाजित करके संबोधित करें। यदि इसके बाद भी आपके पास बहुत सारे पैरामीटर हैं, तो तर्कों को समूहीकृत करने या कुछ मापदंडों को उदाहरण के सदस्यों में बदलने का प्रयास करें।

छोटे वर्गों / विधियों को बड़े स्तर पर प्राथमिकता दें। एकल जिम्मेदारी सिद्धांत को याद रखें।


उदाहरण के सदस्यों और गुणों के साथ समस्या यह है कि वे 1) योग्य होना चाहिए, 2) सेट नहीं किया जा सकता है। एक निर्माता के मामले में, कुछ निश्चित क्षेत्र हैं जिन्हें मैं सुनिश्चित करना चाहता हूं कि कोई उदाहरण मौजूद होने से पहले भरा जाए।
पुनरावर्ती

@recursive - मैं इस बात से असहमत हूं कि फ़ील्ड्स / प्रॉपर्टीज़ को हमेशा लिखने योग्य होना चाहिए। छोटी कक्षाओं के लिए बहुत सारे समय होते हैं जहाँ आसानी से सदस्य समझ जाते हैं।
ब्रायन रासमुसेन

1

नामांकित तर्क एक अच्छा विकल्प है (एक ऐसी भाषा जो उन्हें समर्थन देती है) लंबे समय तक (या उससे भी कम!) पैरामीटर सूचियों की अनुमति देते हुए (निर्माणकर्ताओं के मामले में) वर्ग की संपत्तियों को अस्तित्व में लाने की अनुमति के बिना अपरिवर्तनीय होने की अनुमति देता है। आंशिक रूप से निर्मित अवस्था में।

इस तरह के रिफ्लेक्टर को करने के लिए मैं जो दूसरा विकल्प देखूंगा, वह संबंधित मापदंडों के समूह होंगे जिन्हें स्वतंत्र वस्तु के रूप में बेहतर तरीके से संभाला जा सकता है। उदाहरण के रूप में पहले वाले उत्तर से आयत वर्ग का उपयोग करते हुए, एक्स, वाई, ऊंचाई और चौड़ाई के लिए पैरामीटर लेने वाला कंस्ट्रक्टर x और y को एक पॉइंट ऑब्जेक्ट में ले जा सकता है, जिससे आप रेक्टेंगल के कंस्ट्रक्टर को तीन पैरामीटर पास कर सकते हैं। या थोड़ा आगे बढ़ें और इसे दो पैरामीटर (अपरलॉटफ़ॉइटपॉइंट, लोअरट्राइटपॉइंट) बनाएं, लेकिन यह एक अधिक कट्टरपंथी प्रतिस्थापन होगा।


0

यह इस बात पर निर्भर करता है कि आपके पास किस तरह के तर्क हैं, लेकिन यदि वे बहुत सारे बूलियन मूल्य / विकल्प हैं तो शायद आप एक ध्वज एनम का उपयोग कर सकते हैं?


0

मुझे लगता है कि समस्या उस समस्या के क्षेत्र से गहराई से जुड़ी हुई है जिसे आप वर्ग के साथ हल करने का प्रयास कर रहे हैं।

कुछ मामलों में, एक 7-पैरामीटर कंस्ट्रक्टर एक खराब वर्ग पदानुक्रम का संकेत दे सकता है: उस मामले में, ऊपर वर्णित सहायक संरचना / वर्ग आमतौर पर एक अच्छा दृष्टिकोण होता है, लेकिन फिर आप उन संरचनाओं के भार के साथ समाप्त होते हैं जो सिर्फ संपत्ति बैग हैं और कुछ भी उपयोगी नहीं है। 8-तर्क निर्माणकर्ता यह भी संकेत दे सकता है कि आपकी कक्षा बहुत सामान्य / बहुत उद्देश्य से है, इसलिए इसे वास्तव में उपयोगी होने के लिए बहुत सारे विकल्पों की आवश्यकता है। उस स्थिति में आप या तो क्लास को रिफ्लेक्टर कर सकते हैं या स्थैतिक कंस्ट्रक्टर्स को लागू कर सकते हैं जो वास्तविक कॉम्प्लेक्स कंस्ट्रक्टर्स को छिपाते हैं: उदा। Shniz.NewBaz (फू, बार) वास्तव में सही मापदंडों को पारित करने वाले वास्तविक कंस्ट्रक्टर को बुला सकता है।


0

एक विचार यह है कि किसी वस्तु के बन जाने के बाद कौन से मूल्य पढ़े जाएंगे?

सार्वजनिक रूप से लिखने योग्य गुणों को निर्माण के बाद शायद सौंपा जा सकता है।

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

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

यह वास्तव में एक वस्तु के लिए अजीब होगा, जो वस्तु को पूर्ण राज्य देने के लिए 7 या अधिक मापदंडों की आवश्यकता होती है और सभी प्रकृति में बाहरी रूप से होती है।


0

जब एक क्लैस का एक कंस्ट्रक्टर होता है जो बहुत सारे तर्क देता है, तो यह आमतौर पर एक संकेत है कि इसकी बहुत अधिक जिम्मेदारियां हैं। इसे संभवतः अलग-अलग वर्गों में तोड़ा जा सकता है जो समान कार्यशीलता देने में सहयोग करते हैं।

मामले में आपको वास्तव में जरूरत है कि एक कंस्ट्रक्टर के लिए कई तर्क, बिल्डर पैटर्न आपकी मदद कर सकता है। लक्ष्य अभी भी कंस्ट्रक्टर के लिए सभी तर्कों को पारित करना है, इसलिए इसकी स्थिति शुरू से शुरू की जाती है और यदि आवश्यक हो तो आप अभी भी कक्षा को अपरिवर्तनीय बना सकते हैं।

निचे देखो :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.