नल पॉइंटर्स बनाम नल ऑब्जेक्ट पैटर्न


27

विशेषता: यह एक संबंधित P.SE प्रश्न से बड़ा हुआ

मेरी पृष्ठभूमि C / C ++ में है, लेकिन मैंने जावा में उचित मात्रा में काम किया है और वर्तमान में C # कोड कर रहा हूं। मेरी सी बैकग्राउंड के कारण, पासिंग और लौटाए गए पॉइंटर्स की जाँच दूसरे हाथ से होती है, लेकिन मैं इसे स्वीकार करता हूं कि यह मेरी बात को दर्शाता है।

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

  • तो अशक्त चेक पैटर्न का उपयोग करके एक अशक्त जांच बनाम के पक्ष / विपक्ष क्या हैं?

मैं एनओपी के साथ क्लीनर कॉलिंग कोड देख सकता हूं, लेकिन मैं यह भी देख सकता हूं कि यह कहां छिपी हुई विफलताएं पैदा करेगा जो अन्यथा नहीं उठते हैं। जब तक मैं इसे विकसित नहीं कर रहा होता, तब तक मैं अपने आवेदन को कठिन (उर्फ अपवाद) के रूप में विफल कर देता, जबकि मुझे लगता है कि यह एक जंगली गलती है।

  • अशक्त वस्तु पैटर्न में अशक्त जांच नहीं करने के समान समस्याएं हो सकती हैं?

जिन वस्तुओं पर मैंने काम किया है उनमें से कई वस्तुओं या कंटेनरों को उनके स्वयं के साथ काम किया है। ऐसा लगता है कि मुख्य वस्तु के सभी कंटेनरों की खुद की खाली वस्तुओं की गारंटी देने के लिए मेरे पास एक विशेष मामला होगा। ऐसा लगता है कि घोंसले के शिकार की कई परतों के साथ बदसूरत हो सकता है।


"त्रुटि मामले" नहीं "लेकिन सभी मामलों में एक वैध वस्तु"।

@ ThorbjørnRavnAndersen - अच्छी बात है, और मैंने इस सवाल को संपादित किया है। कृपया मुझे बताएं कि क्या मैं अभी भी एनओपी के आधार को गलत बता रहा हूं।

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

1
@JerryCoffin उस मामले को एक अपवाद द्वारा इंगित किया जाना चाहिए। विचार यह है कि आप कभी भी अशक्त नहीं हो सकते हैं और इसलिए शून्य की जांच करने की आवश्यकता नहीं है।

1
मुझे यह "पैटर्न" पसंद नहीं है। लेकिन संभवत: यह ओवरकेंस्ट्रेटेड रिलेशनल डेटाबेस में निहित है, जहां संपादन योग्य डेटा के मंचन के दौरान विदेशी कुंजियों में विशेष "नल पंक्तियों" को संदर्भित करना आसान है, अंतिम अपडेट से पहले जब सभी विदेशी कुंजी पूरी तरह से शून्य नहीं होती हैं।

जवाबों:


24

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

नल ऑब्जेक्ट पैटर्न उन जगहों के लिए अधिक है जहां एक डिफ़ॉल्ट व्यवहार होता है जिसे उस मामले में लिया जा सकता है जहां ऑब्जेक्ट नहीं मिला है। उदाहरण के लिए निम्नलिखित पर विचार करें:

User* pUser = GetUser( "Bob" );

if( pUser )
{
    pUser->SetAddress( "123 Fake St." );
}

यदि आप एनओपी का उपयोग करते हैं, तो आप लिखेंगे:

GetUser( "Bob" )->SetAddress( "123 Fake St." );

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

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


4
नल ऑब्जेक्ट पैटर्न के लिए उपयोग के मामले से सहमत हैं। लेकिन प्रलयंकारी विफलताओं का सामना करते समय अशक्त क्यों लौटते हैं? अपवाद क्यों नहीं फेंकते?
अपूर्व खुरसिया

2
@MonsterTruck: रिवर्स कि। जब मैं कोड लिखता हूं, तो मैं बाहरी घटकों से प्राप्त अधिकांश इनपुट को मान्य करना सुनिश्चित करता हूं। हालाँकि, आंतरिक कक्षाओं के बीच अगर मैं ऐसा कोड लिखता हूं कि एक वर्ग का कोई फ़ंक्शन NULL को कभी वापस नहीं करेगा, तो कॉलिंग साइड पर मैं केवल "अधिक सुरक्षित" होने के लिए अशक्त चेक नहीं जोड़ूंगा। एकमात्र तरीका यह है कि मूल्य संभवतः NULL हो सकता है यदि मेरे आवेदन में कुछ भयावह तर्क त्रुटि हुई। उस स्थिति में, मैं चाहता हूं कि मेरा कार्यक्रम उस स्थान पर बिल्कुल दुर्घटनाग्रस्त हो जाए क्योंकि तब मुझे एक अच्छा क्रैश डंप फ़ाइल मिलता है और तर्क त्रुटि की पहचान करने के लिए स्टैक और ऑब्जेक्ट स्टेट को वापस करता है।
डीएक्सएम

2
@MonsterTruck: "रिवर्स दैट" से मेरा मतलब है कि जब आप भयावह त्रुटि की पहचान करते हैं, तो स्पष्ट रूप से NULL नहीं लौटाते हैं, लेकिन उम्मीद करते हैं कि NULL केवल कुछ अप्रत्याशित भयावह त्रुटि के कारण हो सकता है।
डीएक्सएम

मुझे अब समझ में आया कि आप क्या आपत्तिजनक त्रुटि का मतलब है - शेष जो वस्तु आवंटन को विफल करने का कारण बनता है। सही? संपादित करें: आपका DXM नहीं कर सकता क्योंकि आपका उपनाम सिर्फ 3 वर्ण लंबा है।
अपूर्व खुरसिया

@MonsterTruck: "@" चीज़ के बारे में अजीब। मैं जानता हूं कि दूसरे लोग पहले भी ऐसा कर चुके हैं। मुझे आश्चर्य है कि अगर उन्होंने हाल ही में वेबसाइट को घुमाया। इसका आवंटन नहीं होना चाहिए। अगर मैं एक वर्ग लिखता हूं और एपीआई का इरादा हमेशा एक वैध वस्तु को वापस करना है, तो मुझे फोन करने वाले को एक सुस्त जांच नहीं करनी होगी। लेकिन भयावह त्रुटि प्रोग्रामर त्रुटि (एक टाइपो जिसके कारण NULL को वापस लौटाया जाता है), या शायद कुछ दौड़ की स्थिति हो सकती है, जो अपने समय से पहले एक वस्तु को मिटा देती है, या शायद कुछ स्टैक भ्रष्टाचार। अनिवार्य रूप से, कोई भी अप्रत्याशित कोड पथ जिसके कारण NULL वापस लौटाया गया था। जब ऐसा होता है आप चाहते हैं ...
DXM

7

तो अशक्त चेक पैटर्न का उपयोग करके एक अशक्त जांच बनाम के पक्ष / विपक्ष क्या हैं?

पेशेवरों

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

विपक्ष

  • साने डिफॉल्ट्स का परिणाम आमतौर पर क्लीनर कोड में होता है।
  • यदि वे जंगल में जाने का प्रबंधन करते हैं, तो सैन डिफाल्ट में आमतौर पर कम तबाही होती है।

यह अंतिम "समर्थक" मुख्य अंतर है (मेरे अनुभव में) जब प्रत्येक को लागू किया जाना चाहिए। "विफलता शोर होना चाहिए?"। कुछ मामलों में, आप चाहते हैं कि विफलता कठिन और तत्काल हो; अगर कुछ परिदृश्य जो कभी भी किसी भी तरह से नहीं होना चाहिए। यदि एक महत्वपूर्ण संसाधन नहीं मिला ... आदि। कुछ मामलों में, आप एक डिफ़ॉल्ट डिफ़ॉल्ट के साथ ठीक हैं क्योंकि यह वास्तव में एक त्रुटि नहीं है : एक शब्दकोश से मूल्य प्राप्त करना, लेकिन कुंजी उदाहरण के लिए गायब है।

किसी भी अन्य डिजाइन निर्णय की तरह, आपकी आवश्यकताओं के आधार पर उतार-चढ़ाव भी होते हैं।


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

क्या अधिक भयावह विफलता है, पैसे भेजने में सक्षम नहीं है या भेजने (और इस तरह अब नहीं है) प्राप्तकर्ता को प्राप्त किए बिना पैसा है और स्पष्ट जांच से पहले इसे नहीं जाना जाता है?
15:

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

@shawnhcorey - क्या?
तेलस्तीन

एक अशक्त वस्तु प्रयोग करने योग्य होनी चाहिए क्योंकि यह एक वास्तविक वस्तु थी। उदाहरण के लिए, IEEE की NaN (संख्या नहीं) पर विचार करें। यदि कोई समीकरण गलत हो जाता है, तो उसे लौटा दिया जाता है। और इसे आगे की गणना के लिए इस्तेमाल किया जा सकता है क्योंकि इस पर कोई भी ऑपरेशन NaN लौटाएगा। यह कोड को सरल बनाता है क्योंकि आपको हर अंकगणितीय ऑपरेशन के बाद NaN के लिए जाँच नहीं करनी है।
shawnhcorey

6

मैं वस्तुनिष्ठ जानकारी का एक अच्छा स्रोत नहीं हूँ, लेकिन विषयवस्तु:

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

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

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

इस रणनीति से श्रेष्ठ भी माना जा सकता है, (पहली बार में समझ की जटिलता में भी श्रेष्ठ), लेकिन उन मामलों के बारे में अधिक है जब आपको बाहरी प्रकार के बाहरी पहलुओं को मॉडल करने की आवश्यकता होती है, जबकि नल ऑब्जेक्ट आंतरिक पहलू। इसलिए NonExistingUser शायद monad if_existing के रूप में बेहतर रूप से तैयार किया गया है।

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


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

3

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

इसके बजाय एक सूचक को असंबद्ध मेमोरी में वापस लाने के बजाय, भाषा ऑब्जेक्ट को वापस कर देगी Nil। यह इस तथ्य से सुगम है कि भाषा गतिशील रूप से टाइप की गई है। एक फ़ंक्शन को हमेशा वापस लौटने की गारंटी नहीं है intNilअगर यह करने की जरूरत है तो यह वापस आ सकता है। वैधानिक रूप से टाइप की गई भाषा में ऐसा करना अधिक कठिन और कठिन हो जाता है, क्योंकि आप स्वाभाविक रूप से किसी भी वस्तु पर लागू होने की उम्मीद करते हैं जिसे संदर्भ द्वारा कहा जा सकता है। आपको किसी भी ऑब्जेक्ट के अशक्त संस्करण को लागू करना शुरू करना होगा जो शून्य हो सकता है (या स्काला / हास्केल मार्ग पर जा सकता है और किसी प्रकार का "हो सकता है" रैपर)।

MrLister ने इस तथ्य को सामने लाया कि जब आप पहली बार इसे बनाते हैं तो C ++ में आपको प्रारंभिक संदर्भ / सूचक की गारंटी नहीं दी जाती है। कच्चे नल सूचक के बजाय एक समर्पित अशक्त वस्तु होने से, आप कुछ अच्छी अतिरिक्त सुविधाएँ प्राप्त कर सकते हैं। रूबी Nilमें एक to_s(toString) फ़ंक्शन शामिल है, जिसके परिणामस्वरूप रिक्त पाठ होता है। यह बहुत अच्छा है यदि आप एक स्ट्रिंग पर एक अशक्त वापसी पाने का मन नहीं करते हैं, और दुर्घटनाग्रस्त हुए बिना इसे रिपोर्ट करना छोड़ना चाहते हैं। ओपन क्लासेस आपको Nilज़रूरत पड़ने पर आउटपुट को मैन्युअल रूप से ओवरराइड करने की भी अनुमति देते हैं ।

यह सब नमक के एक दाने के साथ लिया जा सकता है। मेरा मानना ​​है कि एक गतिशील भाषा में, अशक्त वस्तु को सही तरीके से उपयोग किए जाने पर एक महान सुविधा हो सकती है। दुर्भाग्य से, इसका सबसे अधिक उपयोग करने से आपको आधार कार्यक्षमता को संपादित करने की आवश्यकता हो सकती है, जो आपके प्रोग्राम को इसके अधिक मानक रूपों के साथ काम करने के लिए उपयोग किए जाने वाले लोगों को समझने और बनाए रखने के लिए कठिन बना सकता है।


1

कुछ अतिरिक्त दृष्टिकोण के लिए, ऑब्जेक्टिव-सी पर एक नज़र डालें, जहां एक नल ऑब्जेक्ट के बजाय ऑब्जेक्ट की तरह शून्य प्रकार के कार्यों का मूल्य होता है। एक शून्य ऑब्जेक्ट पर भेजे गए किसी भी संदेश का कोई प्रभाव नहीं होता है और 0 / 0.0 / NO (गलत) / NULL / nil का मान लौटाता है, जो भी विधि का वापसी मान प्रकार है। यह MacOS X और iOS प्रोग्रामिंग में बहुत व्यापक पैटर्न के रूप में उपयोग किया जाता है, और आमतौर पर तरीकों को डिज़ाइन किया जाएगा ताकि एक शून्य वस्तु 0 पर वापस लौटना एक उचित परिणाम हो।

उदाहरण के लिए, यदि आप जांचना चाहते हैं कि क्या एक स्ट्रिंग में एक या अधिक वर्ण हैं, तो आप लिख सकते हैं

aString.length > 0

और एक उचित परिणाम मिलता है अगर aString == nil, क्योंकि एक गैर-मौजूदा स्ट्रिंग में कोई वर्ण नहीं है। लोगों को इसकी आदत पड़ने में थोड़ा समय लगता है, लेकिन दूसरी भाषाओं में हर जगह नील मूल्यों की जाँच करने की आदत डालने में मुझे थोड़ा समय लगेगा।

(NSNull की एक सिंगलटन ऑब्जेक्ट भी है जो काफी भिन्न व्यवहार करती है और अभ्यास में बहुत अधिक उपयोग नहीं की जाती है)।


ऑब्जेक्टिव-सी ने नल ऑब्जेक्ट / नल पॉइंटर चीज़ को वास्तव में धुंधला बना दिया। NULL को nilप्राप्त करें #defineऔर एक अशक्त वस्तु की तरह व्यवहार करें। यह एक रनटाइम सुविधा है जबकि C # / Java नल पॉइंटर्स त्रुटियों का उत्सर्जन करता है।
मैक्सथन चान

0

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

पहले, आइए desiderata को सूचीबद्ध करें फिर सोचें कि हम इसे कुछ भाषाओं में कैसे व्यक्त कर सकते हैं (C ++, OCaml, Python)

  1. संकलन समय पर किसी डिफ़ॉल्ट वस्तु का अवांछित उपयोग करना।
  2. यह स्पष्ट करें कि कोड को पढ़ते समय किसी दिए गए मूल्य में संभावित रूप से चूक हुई है या नहीं।
  3. हमारे समझदार डिफ़ॉल्ट मान चुनें, यदि लागू हो, एक बार प्रति प्रकार । निश्चित रूप से हर कॉल साइट पर संभावित रूप से अलग डिफ़ॉल्ट नहीं चुना जाता है
  4. grepसंभावित गलतियों के शिकार के लिए स्थैतिक विश्लेषण टूल या मानव के लिए इसे आसान बनाएं ।
  5. कुछ अनुप्रयोगों के लिए , प्रोग्राम को सामान्य रूप से जारी रखना चाहिए अगर अप्रत्याशित रूप से डिफ़ॉल्ट मान दिया जाए। अन्य अनुप्रयोगों के लिए , यदि डिफ़ॉल्ट रूप से, सूचनात्मक तरीके से आदर्श रूप में दिया गया है, तो कार्यक्रम को तुरंत रोक देना चाहिए।

मुझे लगता है कि अशक्त वस्तु पैटर्न और अशक्त बिंदुओं के बीच तनाव (5) से आता है। यदि हम त्रुटियों का शीघ्र पता लगा सकते हैं, हालाँकि, (5) मूट हो जाता है।

आइए इस भाषा पर विचार करें:

सी ++

मेरी राय में, एक C ++ वर्ग आमतौर पर डिफ़ॉल्ट-निर्माण योग्य होना चाहिए क्योंकि यह पुस्तकालयों के साथ बातचीत को आसान बनाता है और कंटेनरों में वर्ग का उपयोग करना आसान बनाता है। यह वंशानुक्रम को भी सरल करता है क्योंकि आपको यह सोचने की आवश्यकता नहीं है कि किस सुपरक्लास कंस्ट्रक्टर को कॉल करना है।

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

मैं एक फ़ैक्टरी फ़ंक्शन का उपयोग करने की सलाह दूंगा जो यदि संभव हो तो एक std::optional<MyClass>, std::pair<bool, MyClass>या एक आर-मान संदर्भ देता std::unique_ptr<MyClass>है।

यदि आप चाहते हैं कि आपका फ़ैक्टरी फ़ंक्शन किसी प्रकार के "प्लेसहोल्डर" राज्य को वापस लौटाए, जो डिफ़ॉल्ट-निर्माण से अलग हो MyClass, तो ए का उपयोग करें std::pairऔर यह सुनिश्चित करें कि आपका फ़ंक्शन ऐसा करता है।

यदि फैक्ट्री फ़ंक्शन का एक अद्वितीय नाम है, तो यह नाम के लिए आसान है grepऔर ढलान के लिए देखो। हालाँकि, grepउन मामलों के लिए कठिन है जहाँ प्रोग्रामर को फ़ैक्टरी फ़ंक्शन का उपयोग करना चाहिए था, लेकिन नहीं किया

OCaml

यदि आप OCaml जैसी भाषा का उपयोग कर रहे हैं, तो आप केवल एक optionप्रकार (OCaml मानक पुस्तकालय में) या एक eitherप्रकार का उपयोग कर सकते हैं (मानक पुस्तकालय में नहीं, बल्कि अपना स्वयं का रोल करने में आसान)। या एक defaultableप्रकार (मैं शब्द बना रहा हूं defaultable)।

type 'a option =
    | None
    | Some of 'a

type ('a, 'b) either =
    | Left of 'a
    | Right of 'b

type 'a defaultable =
    | Default of 'a
    | Real of 'a

एक defaultableहै क्योंकि उपयोगकर्ता चाहिए पैटर्न मैच को निकालने के लिए के रूप में ऊपर दिखाए गए एक जोड़ी की तुलना में बेहतर है 'aऔर बस जोड़ी के पहले तत्व नजरअंदाज नहीं कर सकते।

defaultableऊपर दिखाए गए प्रकार के बराबर का उपयोग C ++ में एक std::variantही प्रकार के दो उदाहरणों के साथ किया जा सकता है , लेकिन std::variantयदि दोनों प्रकार समान हैं तो API के भाग अनुपयोगी हैं। यह भी एक अजीब उपयोग है std::variantक्योंकि इसके प्रकार के निर्माता अनाम हैं।

अजगर

आप पायथन के लिए वैसे भी किसी भी संकलन-समय की जाँच नहीं करते हैं। लेकिन, गतिशील रूप से टाइप किया जा रहा है, आम तौर पर ऐसी परिस्थितियां नहीं होती हैं जहां आपको कंपाइलर को गिराने के लिए किसी प्रकार के प्लेसहोल्डर उदाहरण की आवश्यकता होती है।

मैं सिर्फ एक अपवाद फेंकने की सलाह दूंगा जब आप एक डिफ़ॉल्ट उदाहरण बनाने के लिए मजबूर होंगे।

यदि यह स्वीकार्य नहीं है, तो मैं आपके कारखाने के कार्य DefaultedMyClassसे उस विरासत को बनाने MyClassऔर वापस करने की सिफारिश करूंगा । यदि आपको आवश्यकता हो तो डिफ़ॉल्ट रूप से "स्टबिंग आउट" कार्यक्षमता के संदर्भ में आपको लचीलापन मिलता है।

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