क्या हमें पूरे मॉड्यूल उपयोग या सार्वजनिक विधियों के तर्क को मान्य करने की आवश्यकता है?


9

मैंने सुना है कि सार्वजनिक विधियों के तर्कों को मान्य करने की सिफारिश की गई है:

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

जो मुझे परेशान करता है, वह यह है कि गलत तर्क एकमात्र त्रुटि नहीं है जो मॉड्यूल का उपयोग करते समय की जा सकती है। यदि हम अनुशंसाओं का पालन करते हैं और त्रुटि बढ़ाना नहीं चाहते हैं, तो कुछ त्रुटि परिदृश्य यहां हमें तर्क को जोड़ने की आवश्यकता है:

  • आने वाली कॉल - अप्रत्याशित तर्क
  • इनकमिंग कॉल - मॉड्यूल एक गलत स्थिति में है
  • बाहरी कॉल - अप्रत्याशित परिणाम लौटे
  • बाहरी कॉल - अनपेक्षित साइड-इफेक्ट्स (कॉलिंग मॉड्यूल के लिए डबल-एंट्री, अन्य निर्भरता राज्यों को तोड़ना)

मैंने इन सभी स्थितियों को ध्यान में रखते हुए एक विधि के साथ एक सरल मॉड्यूल लिखने की कोशिश की है (क्षमा करें, नहीं- C # लोग):

public sealed class Room
{
    private readonly IDoorFactory _doorFactory;
    private bool _entered;
    private IDoor _door;
    public Room(IDoorFactory doorFactory)
    {
        if (doorFactory == null)
            throw new ArgumentNullException("doorFactory");
        _doorFactory = doorFactory;
    }
    public void Open()
    {
        if (_door != null)
            throw new InvalidOperationException("Room is already opened");
        if (_entered)
            throw new InvalidOperationException("Double entry is not allowed");
        _entered = true;
        _door = _doorFactory.Create();
        if (_door == null)
            throw new IncompatibleDependencyException("doorFactory");
        _door.Open();
        _entered = false;
    }
}

अब यह सुरक्षित है =)

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

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

पूरे मॉड्यूल उपयोग की जाँच करें। क्या हमें यूनिट परीक्षणों की आवश्यकता है? क्या ऐसे कोड के उदाहरण हैं? क्या निर्भरता इंजेक्शन उपयोग में सीमित होना चाहिए (क्योंकि यह अधिक जाँच तर्क का कारण होगा)? क्या उन चेकों को डिबग-टाइम में स्थानांतरित करना व्यावहारिक नहीं है (रिलीज में शामिल नहीं है)?

केवल तर्कों की जाँच करें। मेरे अनुभव से, तर्क की जाँच - विशेष रूप से अशक्त जाँच - कम से कम प्रभावी जाँच है, क्योंकि तर्क त्रुटि शायद ही कभी जटिल गलतियों और त्रुटि बढ़ जाती है। अधिकांश समय आपको NullReferenceExceptionअगली पंक्ति में मिलेगा । तो तर्क जांच इतनी खास क्यों है?

मॉड्यूल उपयोग की जाँच न करें। यह काफी अलोकप्रिय राय है, क्या आप समझा सकते हैं कि क्यों?


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

@Basilevs दिलचस्प ... क्या यह कोड कॉन्ट्रैक्ट्स विचारधारा या कुछ पुराने से है? क्या आप पढ़ने के लिए कुछ सुझा सकते हैं (अपनी टिप्पणी से संबंधित)?
एस्ट्रो

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

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

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

जवाबों:


2

टीएल; डीआर: परिवर्तनशील स्थिति, वर्तमान स्थिति की [वैधता] पर निर्भर है।

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

निम्नलिखित सिद्धांतों पर विचार करें:

  • व्यावहारिक बुद्धि
  • तेजी से असफल
  • सूखी
  • SRP

परिभाषाएं

  • घटक - एपीआई प्रदान करने वाली इकाई
  • क्लाइंट - घटक के एपीआई का उपयोगकर्ता

परस्पर अवस्था

मुसीबत

अनिवार्य भाषाओं में, त्रुटि लक्षण और इसके कारण को भारी उठाने के घंटों से अलग किया जा सकता है। राज्य का भ्रष्टाचार स्वयं को छिपा सकता है और परिणामी अक्षमता को विफल कर सकता है, क्योंकि वर्तमान स्थिति का निरीक्षण भ्रष्टाचार की पूरी प्रक्रिया को प्रकट नहीं कर सकता है और इसलिए, त्रुटि की उत्पत्ति।

समाधान

राज्य के प्रत्येक परिवर्तन को सावधानीपूर्वक गढ़ा और सत्यापित किया जाना चाहिए। उत्परिवर्तनीय स्थिति से निपटने का एक तरीका यह है कि इसे न्यूनतम रखा जाए। इसके द्वारा प्राप्त किया जाता है:

  • प्रकार प्रणाली (const और अंतिम सदस्य घोषणाएं)
  • आक्रमणकारियों का परिचय
  • सार्वजनिक एपीआई के माध्यम से घटक की स्थिति के हर परिवर्तन की पुष्टि करना

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

उदाहरण

// Wrong
class Natural {
    private int number;
    public Natural(int number) {
        this.number = number;
    }
    public int getInt() {
      if (number < 1)
          throw new InvalidOperationException();
      return number;
    }
}

// Right
class Natural {
    private readonly int number;
    /**
     * @param number - positive number
     */
    public Natural(int number) {
      // Going to modify state, verification is required
      if (number < 1)
        throw new ArgumentException("Natural number should be  positive: " + number);
      this.number = number;
    }
    public int getInt() {
      // State is guaranteed by construction and compiler
      return number;
    }
}

दोहराव और जिम्मेदारी सामंजस्य

मुसीबत

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

समाधान

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

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

यदि कोई ऑपरेशन राज्य नहीं बदलता है और राज्य परिवर्तन सत्यापन द्वारा कवर नहीं किया जाता है, तो प्रत्येक तर्क को गहनतम संभव स्तर पर सत्यापित करें।

उदाहरण

class Store {
  private readonly List<int> slots = new List<int>();
  public void putToSlot(int slot, int data) {
    if (slot < 0 || slot >= slots.Count) // Unnecessary, validated by List, only needed for custom error message
      throw new ArgumentException("data");
    slots[slot] = data;
  }
}

class Natural {
   int _number;
   public Natural(int number) {
       if (number < 1)
          number = 1;  //Wrong: client can't rely on argument verification, additional state uncertainity is introduced.  Right: throw new ArgumentException(number);
       _number = number;
   }
}

उत्तर

जब वर्णित सिद्धांतों को प्रश्न में उदाहरण के लिए लागू किया जाता है, तो हम प्राप्त करते हैं:

public sealed class Room
{
    private bool _entered = false;
    // Do not use lazy instantiation if not absolutely necessary, this introduces additional mutable state
    private readonly IDoor _door;
    public Room(IDoorFactory doorFactory)
    {
        // Rely on system null check
        IDoor door = _doorFactory.Create();
        // Modifying own state, verification is required
        if (door == null)
           throw new ArgumentNullException("Door");
        _door = door;
    }
    public void Enter()
    {
        // Room invariants do not guarantee _entered value. Door state is indirectly a part of our state. Verification is required to prevent second door state change below.
        if (_entered)
           throw new InvalidOperationException("Double entry is not allowed");
        _entered = true;     
        // rely on immutability for _door field to be non-null
        // rely on door implementation to control resulting door state       
        _door.Open();            
    }
}

सारांश

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


1

एक वर्ग अपने राज्य के लिए जिम्मेदार होता है। इस हद तक मान्य है कि यह स्वीकार्य स्थिति में चीजों को रखता है या रखता है।

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

नहीं, एक अपवाद न फेंकें, इसके बजाय पूर्वानुमानित व्यवहार दें। राज्य की जिम्मेदारी के लिए कोरोलरी को वर्ग / अनुप्रयोग को व्यावहारिक के रूप में सहनशील बनाना है। उदाहरण के लिए, पास nullकरने के लिए aCollection.Add()? बस जोड़ नहीं है और जा रहा रखने के लिए। nullऑब्जेक्ट बनाने के लिए आपको इनपुट मिलता है ? एक अशक्त वस्तु या एक डिफ़ॉल्ट वस्तु बनाएँ। ऊपर, doorपहले से ही है open? तो क्या, चलते रहो। DoorFactoryतर्क शून्य है? एक नया बनाएँ। जब मैं एक enumमैं हमेशा एक Undefinedसदस्य बनाते हैं । मैं Dictionaryएस का उदार उपयोग करता हूं और enumsचीजों को स्पष्ट रूप से परिभाषित करता हूं; और यह पूर्वानुमानित व्यवहार देने की दिशा में एक लंबा रास्ता तय करता है।

(हाय, निर्भरता इंजेक्शन प्रेमियों!)

हां, हालांकि मैं मापदंडों की घाटी की छाया से चलता हूं, मुझे कोई तर्क नहीं होगा। पूर्ववर्ती के लिए मैं डिफ़ॉल्ट और वैकल्पिक मापदंडों का भी यथासंभव उपयोग करता हूं।

उपरोक्त सभी आंतरिक प्रसंस्करण को जारी रखने की अनुमति देता है। एक विशेष अनुप्रयोग में मेरे पास कई वर्गों में दर्जनों विधियाँ हैं जिनमें केवल एक ही स्थान है जहाँ एक अपवाद रखा गया है। फिर भी, यह अशक्त तर्कों के कारण नहीं है या कि मैं इसे जारी नहीं रख सकता क्योंकि कोड एक "गैर कार्यात्मक" / "अशक्त" वस्तु का निर्माण करता है।

संपादित करें

इसमें मेरी टिप्पणी को उद्धृत करना संपूर्णता है। मुझे लगता है कि 'नल' का सामना करते समय डिज़ाइन को "त्याग" नहीं करना चाहिए। विशेष रूप से एक समग्र वस्तु का उपयोग करना।

हम यहां मुख्य अवधारणाओं / मान्यताओं को भूल रहे हैं - encapsulationऔर single responsibility। वस्तुतः पहले, क्लाइंट-इंटरेक्टिंग परत के बाद कोई अशक्त जाँच नहीं है। कोड सहिष्णु मजबूत है। कक्षाओं को डिफ़ॉल्ट राज्यों के साथ डिज़ाइन किया गया है और इसलिए लिखे बिना काम किया जा रहा है जैसे कि इंटरैक्शन कोड बग-राइडेड है, बदमाश कबाड़। एक समग्र माता-पिता को वैधता का मूल्यांकन करने के लिए बच्चे की परतों तक पहुंचने की ज़रूरत नहीं है (और निहितार्थ से, सभी नुक्कड़ और क्रेन में नल की जांच करें)। माता-पिता को पता है कि बच्चे की डिफ़ॉल्ट स्थिति का क्या मतलब है

अंत संपादित करें


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

1
यदि सभी इंटरफेस को एक सहिष्णु तरीके से डिजाइन किया जाएगा, तो एक दिन, प्रतिबंध त्रुटि के कारण, कार्यक्रम आकस्मिक रूप से जागेंगे और मानवता को नष्ट कर देंगे।
एस्ट्रो

हम यहां मुख्य अवधारणाओं / मान्यताओं को भूल रहे हैं - encapsulationऔर single responsibility। वस्तुतः nullपहली, क्लाइंट-इंटरेक्टिंग परत के बाद कोई जाँच नहीं है । कोड <स्ट्राइक> सहनशील </ स्ट्राइक> मजबूत है। कक्षाओं को डिफ़ॉल्ट राज्यों के साथ डिज़ाइन किया गया है और इसलिए लिखे बिना काम किया जा रहा है जैसे कि इंटरैक्शन कोड बग-राइडेड है, बदमाश कबाड़। एक समग्र माता-पिता को वैधता का मूल्यांकन करने के लिए बच्चे की परतों तक नहीं पहुंचना पड़ता है (और निहितार्थ से, nullसभी नुक्कड़ और क्रेनियों की जांच करें)। माता-पिता को पता है कि बच्चे की डिफ़ॉल्ट स्थिति का क्या मतलब है
रडार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.