विधि के लिए कई तर्क पारित करने के लिए सबसे अच्छा अभ्यास?


94

कभी-कभी, हमें ऐसी विधियाँ लिखनी पड़ती हैं जिनसे कई तर्क मिलते हैं, उदाहरण के लिए:

public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}

जब मैं इस तरह की समस्या का सामना करता हूं, तो मैं अक्सर तर्कों को एक नक्शे में लिख देता हूं।

Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;

......

public void doSomething(Map<Object,Object> params)
{
 // extracting params 
 Object objA = (Object)params.get("objA");
 ......
 }

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

जवाबों:


139

में प्रभावी जावा , अध्याय 7 (तरीके), मद 40 (डिजाइन विधि हस्ताक्षर ध्यान से), बलोच लिखते हैं:

अत्यधिक लंबी पैरामीटर सूचियों को छोटा करने के लिए तीन तकनीकें हैं:

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

अधिक जानकारी के लिए, मैं आपको पुस्तक खरीदने के लिए प्रोत्साहित करता हूं, यह वास्तव में इसके लायक है।


"अत्यधिक लंबा पैरामीटर" क्या है? हम कब कह सकते हैं कि एक विधि में बहुत अधिक पैरामीटर हैं? क्या कोई विशिष्ट संख्या या एक सीमा है?
रेड एम

2
@RedM मैं हमेशा "अधिक लंबा" होने के लिए 3 या 4 से अधिक मापदंडों पर विचार करता हूं
34

2
@jateate एक व्यक्तिगत पसंद है या आप एक आधिकारिक डॉक्टर का अनुसरण कर रहे हैं?
रेड एम

2
@RedM निजी पसंद :)
jtate

2
प्रभावी जावा के तीसरे संस्करण के साथ, यह अध्याय 8 (तरीके), आइटम 51
गैरेथऑवेन

71

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

इसके बजाय एक मॉडल का उपयोग करें। एक वर्ग बनाएं जो उन सभी मापदंडों के लिए एक कंटेनर होगा। इस तरह से आप Java की सुरक्षा को सुरक्षित रखते हैं। आप उस ऑब्जेक्ट को अन्य तरीकों के आसपास भी पारित कर सकते हैं, इसे संग्रह में डाल सकते हैं, आदि।

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


24

यदि आपके पास कई वैकल्पिक पैरामीटर हैं, तो आप धाराप्रवाह एपीआई बना सकते हैं: एकल विधि को विधियों की श्रृंखला से बदलें

exportWithParams().datesBetween(date1,date2)
                  .format("xml")
                  .columns("id","name","phone")
                  .table("angry_robots")
                  .invoke();

स्थिर आयात का उपयोग करके आप आंतरिक धाराप्रवाह एपीआई बना सकते हैं:

... .datesBetween(from(date1).to(date2)) ...

3
क्या होगा यदि हर पैरामीटर की आवश्यकता हो, वैकल्पिक नहीं?
पन्नाधाय

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

13

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

XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);

यहां तक ​​कि अगर आप अपने आप को एक ही पैरामीटर सूची को इतनी बार पास नहीं करते हैं, तो भी यह आसान रीएक्टिंग आपके कोड की पठनीयता में सुधार करेगा, जो हमेशा होता है अच्छा है। यदि आप 3 महीने बाद अपने कोड को देखते हैं, तो बग को ठीक करने या किसी सुविधा को जोड़ने की आवश्यकता होने पर समझना आसान हो जाएगा।

यह निश्चित रूप से एक सामान्य दर्शन है, और चूंकि आपने कोई विवरण नहीं दिया है, इसलिए मैं आपको अधिक विस्तृत सलाह भी नहीं दे सकता। :-)


क्या कचरा संग्रहण एक समस्या होगी?
4

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

इमो, आपके पास भी XXXParameter param = new XXXParameter();उपलब्ध होना चाहिए , और फिर उपयोग करना चाहिए XXXParameter.setObjA(objA); आदि ...
satibel

10

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


8

वस्तुओं का निर्माण करते समय यह अक्सर एक समस्या होती है।

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

तुम भी विधि मंगलाचरण के लिए इसे अनुकूलित कर सकते हैं।

इससे पठनीयता भी बहुत बढ़ जाती है।

public class BigObject
{
  // public getters
  // private setters

  public static class Buider
  {
     private A f1;
     private B f2;
     private C f3;
     private D f4;
     private E f5;

     public Buider setField1(A f1) { this.f1 = f1; return this; }
     public Buider setField2(B f2) { this.f2 = f2; return this; }
     public Buider setField3(C f3) { this.f3 = f3; return this; }
     public Buider setField4(D f4) { this.f4 = f4; return this; }
     public Buider setField5(E f5) { this.f5 = f5; return this; }

    public BigObject build()
    {
      BigObject result = new BigObject();
      result.setField1(f1);
      result.setField2(f2);
      result.setField3(f3);
      result.setField4(f4);
      result.setField5(f5);
      return result;
    }
  }
}

// Usage:
BigObject boo = new BigObject.Builder()
  .setField1(/* whatever */)
  .setField2(/* whatever */)
  .setField3(/* whatever */)
  .setField4(/* whatever */)
  .setField5(/* whatever */)
  .build();

आप सत्यापन तर्क को बिल्डर सेट .. (और निर्माण) में डाल सकते हैं।


यदि आपके कई क्षेत्र हैं, तो आप क्या सलाह देंगे final? यह मुख्य बात है जो मुझे सहायक कार्यों को लिखने से रोकती है। मुझे लगता है कि मैं खेतों को निजी बना सकता हूं और यह सुनिश्चित कर सकता हूं कि मैं उन्हें उस कक्षा के कोड में गलत तरीके से संशोधित नहीं कर रहा हूं, लेकिन मैं कुछ और सुरुचिपूर्ण की उम्मीद कर रहा हूं।
ragerdl

7

एक पैटर्न है जिसे पैरामीटर ऑब्जेक्ट कहा जाता है ।

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


5

आप उस डेटा को रखने के लिए एक वर्ग बना सकते हैं। हालांकि पर्याप्त सार्थक होने की जरूरत है, लेकिन एक नक्शे (OMG) का उपयोग करने से बेहतर है।


मुझे नहीं लगता कि विधि पैरामीटर रखने के लिए कक्षा बनाने के लिए यह आवश्यक है।
सवाई

यदि एक ही पैरामीटर पास करने के कई उदाहरण हैं, तो मैं केवल कक्षा का निर्माण करूँगा। यह संकेत देता है कि पैरामीटर संबंधित हैं और संभवतः वैसे भी एक साथ हैं। यदि आप एक एकल विधि के लिए एक वर्ग बना रहे हैं तो इलाज संभवतः बीमारी से भी बदतर है।
tvanfosson

हाँ - आप संबंधित पार्म्स को डीटीओ या वैल्यू ऑब्जेक्ट में स्थानांतरित कर सकते हैं। क्या बहुविध वैकल्पिक में से कुछ वैकल्पिक हैं अर्थात मुख्य विधि इन अतिरिक्त परिमों के साथ ओवरलोडेड है? ऐसे मामलों में - मुझे लगता है कि यह स्वीकार्य है।
जोस

मेरे कहने का यही मतलब है कि पर्याप्त रूप से सार्थक होना चाहिए।
जोहान्स रुडोल्फ

4

कोड पूरा * कुछ बातों का सुझाव देता है:

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

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


2

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


हाँ उन्हें चाहिए। उदाहरण के लिए, एक बड़े खोज फॉर्म में बहुत से असंबंधित अवरोध हैं और अंकुरण के लिए आवश्यकताएं हैं। आपको currentPageNumber, searchCriteria, pageSize पास करने की आवश्यकता है ...
Sawyer

2

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

एक क्लीनर तरीका एक ऑब्जेक्ट बीन में सभी मापदंडों को समूहित करना होगा लेकिन यह अभी भी पूरी तरह से समस्या को ठीक नहीं करता है।

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

आपको बहुत सारे मापदंडों को भेजने के लिए ऐप के बेहतर डिज़ाइन की ज़रूरत नहीं है।


1

बीन क्लास बनाएं, और सभी मापदंडों (सेटर विधि) को सेट करें और इस बीन ऑब्जेक्ट को विधि में पास करें।


1
  • अपने कोड को देखें, और देखें कि उन सभी मापदंडों को क्यों पारित किया गया है। कभी-कभी यह स्वयं विधि को फिर से भरना संभव है।

  • मानचित्र का उपयोग करने से आपका तरीका कमजोर हो जाता है। क्या होगा यदि कोई आपकी विधि का उपयोग कर एक पैरामीटर नाम को गलत करता है, या एक स्ट्रिंग पोस्ट करता है जहां आपकी विधि एक यूडीटी की उम्मीद करती है?

  • ट्रांसफर ऑब्जेक्ट को परिभाषित करें । यह आपको बहुत कम से कम टाइपिंग प्रदान करेगा; आपके लिए अपने तरीके के बजाय उपयोग के बिंदु पर कुछ सत्यापन करना संभव हो सकता है।



0

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


0

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

  1. मानचित्र - आपके द्वारा उल्लिखित दक्षता की बात है, लेकिन यहां बड़ी समस्या यह है:

    • कॉल करने वालों को पता नहीं है कि आपको किसी
      और चीज़ का हवाला दिए बिना क्या भेजना है ... क्या आपके पास javadocs है जो बताती है कि वास्तव में क्या कुंजी और
      मान का उपयोग किया जाता है? यदि आप करते हैं (जो बहुत अच्छा है), तो बहुत सारे मापदंडों का होना कोई समस्या नहीं है।
    • विभिन्न तर्क प्रकारों को स्वीकार करना बहुत मुश्किल हो जाता है। आप या तो इनपुट मापदंडों को एक प्रकार से प्रतिबंधित कर सकते हैं, या मानचित्र <स्ट्रिंग, ऑब्जेक्ट> का उपयोग कर सकते हैं और सभी मानों को डाल सकते हैं। दोनों विकल्प ज्यादातर समय भयानक होते हैं।
  2. रैपर ऑब्जेक्ट्स - यह समस्या को केवल इसलिए बढ़ा देता है क्योंकि आपको रैपर ऑब्जेक्ट को पहले स्थान पर भरने की आवश्यकता होती है - सीधे आपकी विधि के बजाय, यह पैरामीटर ऑब्जेक्ट के कंस्ट्रक्टर के लिए होगा। यह निर्धारित करने के लिए कि समस्या का बढ़ना उचित है या नहीं, उक्त वस्तु के पुन: उपयोग पर निर्भर करता है। उदाहरण के लिए:

इसका उपयोग नहीं करेंगे: यह केवल पहली कॉल पर एक बार उपयोग किया जाएगा, इसलिए 1 पंक्ति से निपटने के लिए बहुत सारे अतिरिक्त कोड ...?

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    SomeObject i = obj2.callAnotherMethod(a, b, c, h);
    FinalResult j = obj3.callAFinalMethod(c, e, f, h, i);
}

इसका उपयोग कर सकते हैं: यहां, यह थोड़ा अधिक कर सकता है। सबसे पहले, यह 3 विधि कॉल के लिए मापदंडों को कारक कर सकता है। यह अपने आप में 2 अन्य लाइनें भी कर सकता है ... इसलिए यह एक अर्थ में एक राज्य चर बन जाता है ...

{
    AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
    e = h.resultOfSomeTransformation();
    SomeObject i = obj2.callAnotherMethod(a, b, c, d, e, f, g);
    f = i.somethingElse();
    FinalResult j = obj3.callAFinalMethod(a, b, c, d, e, f, g, h, i);
}
  1. बिल्डर पैटर्न - यह मेरे विचार में एक विरोधी पैटर्न है। सबसे वांछनीय त्रुटि से निपटने का तंत्र पहले पता लगाने के लिए है, बाद में नहीं; लेकिन बिल्डर पैटर्न के साथ, लापता के साथ कॉल (प्रोग्रामर ने इसे शामिल करने के लिए नहीं सोचा था) अनिवार्य मापदंडों को संकलन समय से चलाने के लिए स्थानांतरित किया जाता है। बेशक अगर प्रोग्रामर जानबूझकर अशक्त या इस तरह के स्लॉट में रखता है, तो वह रनटाइम होगा, लेकिन फिर भी कुछ त्रुटियों को पहले से पकड़ना प्रोग्रामर के लिए खानपान के लिए बहुत बड़ा लाभ है जो उस पद्धति के पैरामीटर नामों को देखने से इनकार करते हैं जो वे बुला रहे हैं। मुझे यह केवल उचित लगता है जब बड़ी संख्या में वैकल्पिक मापदंडों के साथ काम किया जाता है , और फिर भी, लाभ सबसे अच्छा है। मैं बिल्डर "पैटर्न" के बहुत खिलाफ हूं।

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

इसके अलावा, विकल्प 2 और 3, यदि व्यापक रूप से व्यापक रूप से अपनाया गया है, तो बड़ी मात्रा में डुप्लिकेट कोड के कारण रखरखाव के संदर्भ में दीर्घकालिक नकारात्मक प्रभाव पड़ता है। जितना अधिक कोड होता है, उतना ही बनाए रखने के लिए होता है, इसे बनाए रखने के लिए अधिक समय और पैसा खर्च होता है।

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