अत्यधिक विधि अधिभार से कैसे बचें?


16

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

यह लंबे विकास (विरासत कोड) और इस सोच का परिणाम है (मेरा मानना ​​है):

" एक ऐसा तरीका है जो M करता है। A. मुझे A + B. करने की आवश्यकता है। ठीक है, मुझे पता है ... मैं M में एक नया पैरामीटर जोड़ूंगा, उसके लिए एक नया तरीका बनाऊंगा, M से नई विधि में कोड स्थानांतरित करूंगा। एक और पैरामीटर के साथ, वहां पर A + B करें और नए पैरामीटर के डिफ़ॉल्ट मान के साथ M से नई विधि को कॉल करें। "

यहाँ एक उदाहरण है (जावा जैसी भाषा में):

class DocumentHome {

  (...)

  public Document createDocument(String name) {
    // just calls another method with default value of its parameter
    return createDocument(name, -1);
  }

  public Document createDocument(String name, int minPagesCount) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false);
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank) {
    // just calls another method with default value of its parameter
    return createDocument(name, minPagesCount, false, "");
  }

  public Document createDocument(String name, int minPagesCount, boolean firstPageBlank, String title) {
    // here the real work gets done
    (...)
  }

  (...)
}

मुझे ऐसा लगता है कि यह गलत है। न केवल यह कि हम इस तरह के नए मापदंडों को हमेशा के लिए जोड़कर नहीं रख सकते हैं, लेकिन सभी तरीकों के बीच निर्भरता के कारण कोड को बदलना / बदलना मुश्किल है।

यहाँ कुछ तरीके हैं कि यह कैसे बेहतर किया जाए:

  1. एक पैरामीटर ऑब्जेक्ट का परिचय दें:

    class DocumentCreationParams {
    
      String name;
      int minPagesCount;
      boolean firstPageBlank;
      String title;
    
      (...)
    }
    
    class DokumentHome {
    
      public Document createDocument(DocumentCreationParams p) {
        // here the real work gets done
        (...)
      }
    }
    
  2. DocumentHomeकॉल करने से पहले ऑब्जेक्ट को पैरामीटर सेट करेंcreateDocument()

      @In
      DocumentHome dh = null;
    
      (...)
    
      dh.setName(...);
      dh.setMinPagesCount(...);
      dh.setFirstPageBlank(...);
    
      Document newDocument = dh.createDocument();
    
  3. अलग-अलग तरीकों से काम को अलग करें और आवश्यकतानुसार कॉल करें:

      @In
      DocumentHome dh = null;
    
      Document newDocument = dh.createDocument();
      dh.changeName(newDocument, "name");
      dh.addFirstBlankPage(newDocument);
      dh.changeMinPagesCount(new Document, 10);
    

मेरे सवाल:

  1. वर्णित समस्या वास्तव में एक समस्या है?
  2. सुझाए गए समाधानों के बारे में आप क्या सोचते हैं? आप किसे पसंद करेंगे (आपके अनुभव के आधार पर)?
  3. क्या आप किसी अन्य समाधान के बारे में सोच सकते हैं?

1
आप किस भाषा को लक्षित कर रहे हैं या यह सिर्फ सेंट जेनेल है?
केर्न्ड

कोई विशेष भाषा नहीं, बस सामान्य। यह दिखाने के लिए स्वतंत्र महसूस करें कि अन्य भाषाओं की विशेषताएं इसमें कैसे मदद कर सकती हैं।
युतुस Y

जैसे मैंने कहा कि यहाँ प्रोग्रामर.स्टैकएक्सचेंज . com/questions/235096/… C # और C ++ में कुछ विशेषताएं हैं।
किन्नर

यह बहुत स्पष्ट है कि यह सवाल हर उस भाषा पर लागू होता है जो इस तरह के तरीके को ओवरलोडिंग का समर्थन करती है।
डॉक्टर ब्राउन

1
@DocBrown ठीक है, लेकिन हर भाषा एक ही विकल्प का समर्थन नहीं करती है;)
Knerd

जवाबों:


20

शायद बिल्डर पैटर्न की कोशिश करो ? (ध्यान दें: काफी यादृच्छिक Google परिणाम :)

var document = new DocumentBuilder()
                   .FirstPageBlank()
                   .Name("doc1final(2).doc")
                   .MinimumNumberOfPages(4)
                   .Build();

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

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

आपके अन्य प्रयास मुझे ऐसे दिखते हैं जैसे कोई बिल्डर पैटर्न के लिए पहुंच रहा है, लेकिन वहां नहीं पहुंच रहा है :)


धन्यवाद। आपका उत्तर समाधान संख्या हो सकता है। 4, हाँ। मैं इस अंदाज में जवाब तलाश रहा हूं: 'मेरे अनुभव में यह आगे बढ़ता है ... और तय किया जा सकता है ...'। या 'जब मैं इसे अपने सहकर्मी के कोड में देखता हूं, तो मैं उसे इसके बजाय ... का सुझाव देता हूं।'
युतस

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

1

एक पैरामीटर ऑब्जेक्ट का उपयोग करना तरीकों से अधिक भार से बचने (अत्यधिक) से बचने का एक अच्छा तरीका है:

  • यह कोड को साफ करता है
  • कार्यक्षमता से डेटा को अलग करता है
  • कोड को अधिक रखरखाव योग्य बनाता है

हालाँकि, मैं इसके साथ बहुत दूर नहीं जाऊँगा।

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

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

बस मेरे 2 सेंट।


0

मैं ईमानदारी से कोड के साथ एक बड़ी समस्या नहीं देखता हूं। C # और C ++ में आप वैकल्पिक मापदंडों का उपयोग कर सकते हैं, यह एक विकल्प होगा, लेकिन जहां तक ​​मुझे पता है कि जावा उस तरह के मापदंडों का समर्थन नहीं करता है।

C # में आप सभी ओवरलोड को निजी बना सकते हैं और वैकल्पिक मापदंडों के साथ एक विधि सामान को कॉल करने के लिए सार्वजनिक है।

आपके प्रश्न भाग 2 का उत्तर देने के लिए, मैं पैरामीटर ऑब्जेक्ट या यहां तक ​​कि एक शब्दकोश / हशपैप ले जाऊंगा।

इस तरह:

class DokumentHome {

  public Document createDocument(Map<string, object> params) {
    if (params.containsKey("yourkey")) {
       // do that
    }
    // here the real work gets done
    (...)
  }
}

अस्वीकरण के रूप में, मैं पहले C # और जावास्क्रिप्ट प्रोग्रामर और फिर जावा प्रोग्रामर हूं।


4
इसका एक समाधान है, लेकिन मुझे नहीं लगता कि इसका एक अच्छा समाधान (कम से कम, ऐसे संदर्भ में नहीं, जहां कि प्रकार की अपेक्षा की जाती है)।
डॉक्टर ब्राउन

ये सही है। इस तरह के मामलों के कारण, मुझे ओवरलोडेड तरीके या वैकल्पिक पैरामीटर पसंद हैं।
14

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

शब्दकोश का उपयोग केवल सही मायने में वैकल्पिक मापदंडों के लिए करें ताकि बुनियादी उपयोग के मामलों को बहुत अधिक प्रलेखन पढ़ने की आवश्यकता न हो।
user949300

0

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

आपके मामले में, मेरे पास निम्नलिखित विधियों के साथ एक बिल्डर होगा:

SetTitle (Document document) { ... }
SetFirstPageBlank (Document document) { ... }
SetMinimumPageCount (Document document) { ... }

फिर आप निम्न तरीके से बिल्डर का उपयोग कर सकते हैं:

_builder.SetTitle(document);
_builder.SetFirstPageBlank(document);

साइड नोट पर, मुझे कुछ सरल अतिभारों से ऐतराज नहीं है - नरक क्या है, .NET फ्रेमवर्क HTML सहायकों के साथ सभी जगह उनका उपयोग करता है। हालाँकि, मैं फिर से मूल्यांकन करूँगा कि मैं क्या कर रहा हूँ अगर मुझे हर विधि के लिए दो से अधिक मापदंडों को पारित करना है।


0

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

यदि आप दस्तावेज़ बनाने के लिए सिर्फ एक साधारण बिल्डर बनाते हैं और इस कोड को एप्लिकेशन के विभिन्न भागों (कक्षाओं) पर फैलाते हैं, तो यह कठिन होगा:

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

लेकिन यह ओपी प्रश्न का उत्तर नहीं देता है।

अधिभार का विकल्प

कुछ विकल्प:

  • विधि नाम का नाम बदलें : यदि वही विधि नाम कुछ भ्रम पैदा कर रहा है, तो एक सार्थक नाम बनाने की विधियों को बेहतर से बेहतर बनाने का प्रयास करें createDocument, जैसे:createLicenseDriveDocument , createDocumentWithOptionalFields,, आदि बेशक, यह आप विशाल विधि के नाम को जन्म दे सकता तो यह नहीं है सभी मामलों के लिए एक समाधान।
  • स्थैतिक तरीकों का उपयोग करें । ऊपर दिए गए पहले विकल्प की तुलना में यह दृष्टिकोण एक जैसा है। आप प्रत्येक मामले में एक सार्थक नाम का उपयोग कर सकते हैं और एक स्थिर विधि से दस्तावेज़ को तुरंत निकाल सकते हैंDocument , जैसे Document.createLicenseDriveDocument():।
  • एक आम इंटरफ़ेस बनाएँ : आप एक ही विधि कहा जाता है बना सकते हैं createDocument(InterfaceDocument interfaceDocument)और विभिन्न बनाने कार्यान्वयन के लिए InterfaceDocument। उदाहरण के अनुसार: createDocument(new DocumentMinPagesCount("name"))। बेशक, आपको प्रत्येक मामले के लिए एक कार्यान्वयन की आवश्यकता नहीं है, क्योंकि आप प्रत्येक कार्यान्वयन पर एक से अधिक निर्माणकर्ता बना सकते हैं, कुछ फ़ील्ड्स को समूहीकृत करना जो उस कार्यान्वयन पर समझ में आता है। इस पैटर्न को टेलिस्कोपिंग कंस्ट्रक्टर कहा जाता है

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

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