मैंने कुछ आवाज़ें यह कहते हुए सुनी हैं कि तरीकों से लौटाए गए शून्य मान के लिए जाँच करना खराब डिज़ाइन है। मैं इसके लिए कुछ कारण सुनना चाहूंगा।
स्यूडोकोड:
variable x = object.method()
if (x is null) do something
मैंने कुछ आवाज़ें यह कहते हुए सुनी हैं कि तरीकों से लौटाए गए शून्य मान के लिए जाँच करना खराब डिज़ाइन है। मैं इसके लिए कुछ कारण सुनना चाहूंगा।
स्यूडोकोड:
variable x = object.method()
if (x is null) do something
जवाबों:
अशक्त नहीं लौटने के पीछे तर्क यह है कि आपको इसके लिए जांच करने की आवश्यकता नहीं है और इसलिए आपके कोड को वापसी मूल्य के आधार पर एक अलग पथ का पालन करने की आवश्यकता नहीं है । आप नल ऑब्जेक्ट पैटर्न की जांच करना चाहते हैं जो इस पर अधिक जानकारी प्रदान करता है।
उदाहरण के लिए, अगर मैं जावा में एक विधि को परिभाषित करता हूं जो एक संग्रह लौटाता है तो मैं आमतौर पर रिक्त संग्रह (यानी Collections.emptyList()
) को शून्य के बजाय वापस करना पसंद करूंगा क्योंकि इसका मतलब है कि मेरा क्लाइंट कोड क्लीनर है; जैसे
Collection<? extends Item> c = getItems(); // Will never return null.
for (Item item : c) { // Will not enter the loop if c is empty.
// Process item.
}
... जो क्लीनर है:
Collection<? extends Item> c = getItems(); // Could potentially return null.
// Two possible code paths now so harder to test.
if (c != null) {
for (Item item : c) {
// Process item.
}
}
null
एक "कंटेनर" है, जिसमें वस्तुओं के लिए शून्य या एक संकेत होता है। (यह निश्चित रूप से है कि यह Maybe
उन मामलों में हास्केल द्वारा स्पष्ट रूप से कैसे तैयार किया गया है , और ऐसा करने के लिए सभी बेहतर है।)
यहाँ कारण है।
में स्वच्छ कोड रॉबर्ट मार्टिन द्वारा वे लिखते हैं कि लौटने अशक्त बुरा डिजाइन जब आप के बजाय लौट सकते हैं, कहते हैं, खाली सरणी है। चूंकि अपेक्षित परिणाम एक सरणी है, क्यों नहीं? यह आपको बिना किसी अतिरिक्त शर्त के परिणाम में पुनरावृति करने में सक्षम करेगा। यदि यह पूर्णांक है, तो शायद 0 पर्याप्त होगा, यदि यह एक हैश, खाली हैश है। आदि।
आधार कॉलिंग कोड को तुरंत मुद्दों से निपटने के लिए मजबूर नहीं करना है। कॉलिंग कोड शायद उनके साथ खुद को चिंता नहीं करना चाहता। यही कारण है कि कई मामलों में अपवाद शून्य से बेहतर है।
अशक्त लौटने के अच्छे उपयोग:
खराब उपयोग: जैसे असाधारण स्थितियों को बदलने या छिपाने की कोशिश करना:
उन मामलों में अपवाद छोड़ना अधिक पर्याप्त है:
हालाँकि, अपवाद का उपयोग सामान्य प्रोग्राम संचालन स्थितियों को संभालने के लिए नहीं किया जाना चाहिए जैसे:
boolean login(String,String)
ठीक लगता है और ऐसा ही होता हैAuthenticationContext createAuthContext(String,String) throws AuthenticationException
हां, NULL लौटना एक भयानक डिजाइन है , वस्तु-उन्मुख दुनिया में। संक्षेप में, NULL उपयोग की ओर जाता है:
विस्तृत विवरण के लिए इस ब्लॉग पोस्ट की जाँच करें: http://www.yegor256.com/2014/05/13/why-null-is-bad.html । मेरी पुस्तक एलिगेंट ऑब्जेक्ट्स , धारा 4.1 में अधिक।
bool TryGetBlah(out blah)
या FirstOrNull()
या MatchOrFallback(T fallbackValue)
।
isEmpty()
) और केवल अगर यह सच है तो विधि को कॉल करें। लोग दूसरे के खिलाफ तर्क देते हैं कि यह खराब प्रदर्शन है - लेकिन जैसा कि यूनिक्स दर्शन कहते हैं, "मशीन समय पर मानव समय को महत्व देते हैं" (यानी तुच्छ धीमा प्रदर्शन डेवलपर्स डिबगिंग कोड की तुलना में कम समय बर्बाद करता है जो कि गंभीर त्रुटियां दे रहा है)।
कौन कहता है कि यह खराब डिजाइन है?
नल की जांच करना एक आम बात है, यहां तक कि प्रोत्साहित भी किया जाता है, अन्यथा आप हर जगह NullReferenceException का जोखिम चलाते हैं। जब आप की आवश्यकता नहीं है, तो इसे अपवाद से फेंकने की अपेक्षा त्रुटि को संभालना बेहतर है।
अब तक आपने जो कहा है, उसके आधार पर, मुझे लगता है कि पर्याप्त जानकारी नहीं है।
एक CreateWidget () विधि से अशक्त लौटना बुरा लगता है।
FindFooInBar () विधि से अशक्त लौटना ठीक लगता है।
Create...
एक नया उदाहरण देता है, या फेंकता है ; Get...
एक अपेक्षित मौजूदा उदाहरण देता है, या फेंकता है ; GetOrCreate...
यदि कोई मौजूद नहीं है या फेंकता है, तो एक मौजूदा उदाहरण, या नया उदाहरण लौटाता है ; Find...
एक मौजूदा उदाहरण देता है, अगर यह मौजूद है, याnull
। संग्रह क्वेरीज़ के लिए - Get...
हमेशा एक संग्रह लौटाता है, जो कोई मेल नहीं मिलने पर खाली होता है।
यह उस भाषा पर निर्भर करता है जिसका आप उपयोग कर रहे हैं। यदि आप C # जैसी भाषा में हैं, जहाँ किसी मान की कमी को इंगित करने का मुहावरेदार तरीका है, अशक्त लौटना, तो यदि आपके पास कोई मान नहीं है, तो अशक्त लौटना एक अच्छा डिज़ाइन है। वैकल्पिक रूप से, हास्केल जैसी भाषाओं में जो इस मामले के लिए शायद मुदित का उपयोग करते हैं, फिर अशक्त लौट आना एक बुरा डिजाइन होगा (यदि यह भी संभव था)।
null
सी # और जावा जैसी भाषाओं की परिभाषा अक्सर अतिभारित होती है और डोमेन में कुछ अर्थ दिया जाता है। यदि आप null
भाषा स्पेक्स में कीवर्ड को देखते हैं, तो इसका मतलब है "अमान्य सूचक"। यह संभावना किसी भी समस्या डोमेन में कुछ भी नहीं है।
null
या "आरंभीकृत नहीं है"null
यदि आप सभी उत्तरों को पढ़ते हैं तो यह स्पष्ट हो जाता है कि इस प्रश्न का उत्तर किस तरह की विधि पर निर्भर करता है।
सबसे पहले, जब कुछ असाधारण होता है (IOproblem आदि), तार्किक रूप से अपवादों को फेंक दिया जाता है। जब वास्तव में कुछ असाधारण होता है तो शायद एक अलग विषय के लिए कुछ होता है।
जब भी किसी विधि से अपेक्षा की जाती है कि संभवतः कोई परिणाम नहीं है दो श्रेणियां हैं:
अनटिल में हमारे पास यह बताने का एक आधिकारिक तरीका है कि कोई फ़ंक्शन शून्य हो सकता है या वापस नहीं आ सकता है, मैं ऐसा करने के लिए नामकरण सम्मेलन करने की कोशिश करता हूं।
जैसे आपके पास ट्राई समथिंग () कन्वेंशन फेल होने की उम्मीद के तरीके हैं, मैं अक्सर अपने तरीकों को नाम देता हूं सेफ समथिंग () जब विधि शून्य के बजाय एक तटस्थ परिणाम देता है।
मैं अभी तक नाम के साथ पूरी तरह से ठीक नहीं हूं, लेकिन कुछ भी बेहतर नहीं कर सका। इसलिए मैं इसके लिए अभी दौड़ रहा हूं।
मेरा इस क्षेत्र में एक सम्मेलन है जिसने मुझे अच्छी सेवा दी है
एकल आइटम प्रश्नों के लिए:
Create...
एक नया उदाहरण देता है, या फेंकता हैGet...
एक अपेक्षित मौजूदा उदाहरण देता है, या फेंकता हैGetOrCreate...
यदि कोई मौजूद नहीं है, या फेंकता है, तो एक मौजूदा उदाहरण, या नया उदाहरण लौटाता हैFind...
एक मौजूदा उदाहरण देता है, अगर यह मौजूद है, याnull
संग्रह प्रश्नों के लिए:
Get...
हमेशा एक संग्रह लौटाता है, जो खाली है अगर कोई मिलान नहीं [1] आइटम पाए जाते हैं[१] कुछ मानदंड, स्पष्ट या निहित, फ़ंक्शन नाम या मापदंडों के रूप में दिए गए।
Get
जब मैं उम्मीद करता हूं कि मैं इसका उपयोग करूंगा , तो अगर यह नहीं है, तो यह एक त्रुटि है और मैं फेंक देता हूं - मुझे वापसी मूल्य की जांच करने की आवश्यकता नहीं है। मैं उपयोग करता हूं Find
अगर मुझे वास्तव में पता नहीं है कि यह वहां है या नहीं - तो मुझे वापसी मूल्य की जांच करने की आवश्यकता है।
अपवाद के लिए कर रहे हैं अपवाद अल परिस्थितियों के लिए हैं।
यदि आपके फ़ंक्शन का उद्देश्य किसी दिए गए ऑब्जेक्ट से जुड़ी विशेषता को ढूंढना है, और उस ऑब्जेक्ट में ऐसी कोई विशेषता नहीं है, तो अशक्त वापस आना उचित हो सकता है। यदि ऑब्जेक्ट मौजूद नहीं है, तो अपवाद फेंकना अधिक उपयुक्त हो सकता है। यदि फ़ंक्शन विशेषताओं की सूची को वापस करने के लिए है, और वापस लौटने के लिए कोई भी नहीं है, तो खाली सूची वापस करने से समझ में आता है - आप सभी शून्य विशेषताओं को वापस कर रहे हैं।
यदि ऐसा करना सार्थक है, तो यह किसी तरह से सार्थक है:
public String getEmployeeName(int id){ ..}
इस तरह के मामले में अगर आईडी किसी मौजूदा इकाई के अनुरूप नहीं है, तो यह वापस लौटने के लिए सार्थक है, क्योंकि यह आपको उस मामले को अलग करने की अनुमति देता है जहां कोई मैच वैध त्रुटि से नहीं मिला था।
लोग सोच सकते हैं कि यह बुरा है क्योंकि इसे "विशेष" रिटर्न मान के रूप में दुरुपयोग किया जा सकता है जो एक त्रुटि स्थिति को इंगित करता है, जो इतना अच्छा नहीं है, एक फ़ंक्शन से रिटर्न कोड त्रुटि की तरह थोड़ा सा है, लेकिन भ्रमित क्योंकि उपयोगकर्ता के लिए वापसी की जांच करना है अशक्त, उपयुक्त अपवादों को पकड़ने के बजाय, उदा
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
यह जरूरी नहीं कि एक खराब डिजाइन है - इतने सारे डिजाइन निर्णयों के साथ, यह निर्भर करता है।
यदि विधि का परिणाम कुछ ऐसा है जिसका सामान्य उपयोग में अच्छा परिणाम नहीं होता है, तो वापस लौटना ठीक है:
object x = GetObjectFromCache(); // return null if it's not in the cache
यदि वास्तव में हमेशा एक अशक्त परिणाम होना चाहिए, तो अपवाद फेंकना बेहतर हो सकता है:
try {
Controller c = GetController(); // the controller object is central to
// the application. If we don't get one,
// we're fubar
// it's likely that it's OK to not have the try/catch since you won't
// be able to really handle the problem here
}
catch /* ... */ {
}
Optional<>
कुछ परिदृश्यों के लिए, आप एक असफलता को जल्द से जल्द नोटिस करना चाहते हैं।
NULL के खिलाफ जाँच करना और प्रोग्रामर त्रुटियों के लिए जोर लगाना या (विफलता के मामले में उपयोगकर्ता या कॉलर त्रुटियों के लिए) फेंकने का मतलब यह हो सकता है कि बाद में क्रैश को ट्रैक करना मुश्किल है, क्योंकि मूल विषम मामला नहीं मिला था।
इसके अलावा, त्रुटियों को अनदेखा करने से सुरक्षा कारनामे हो सकते हैं। शायद नल-नेस इस तथ्य से आया था कि एक बफर ओवरराइट या पसंद किया गया था। अब, आप क्रैश नहीं कर रहे हैं , जिसका अर्थ है कि शोषणकर्ता को आपके कोड को निष्पादित करने का मौका है।
आप अशक्त लौटने के लिए क्या विकल्प देखते हैं?
मैं दो मामले देखता हूं:
इस मामले में हम कर सकते हैं: वापसी नल या एक फेंक दिया (जाँच) अपवाद (या शायद एक आइटम बनाने के लिए और इसे वापस)
इस मामले में हम अशक्त लौट सकते हैं, एक खाली सूची वापस कर सकते हैं या एक अपवाद फेंक सकते हैं।
मेरा मानना है कि वापसी अशक्त विकल्प की तुलना में कम अच्छा हो सकता है क्योंकि ग्राहक को शून्य, प्रोग्रामर भूलने और कोड की जांच करने के लिए याद रखने की आवश्यकता होती है
x = find();
x.getField(); // bang null pointer exception
जावा में, एक चेक किए गए अपवाद को फेंकना, RecordNotFoundException, कंपाइलर को क्लाइंट को केस से निपटने के लिए याद दिलाने की अनुमति देता है।
मुझे लगता है कि खाली सूचियों को लौटाने वाली खोजें काफी सुविधाजनक हो सकती हैं - बस सूची की सभी सामग्रियों के साथ प्रदर्शन को आबाद करें, ओह यह खाली है, कोड "बस काम करता है"।
कभी-कभी, NULL को वापस करना सही बात है, लेकिन विशेष रूप से जब आप विभिन्न प्रकारों (सरणियों, सूचियों, तारों, व्हाट्स-हैव यू) के अनुक्रमों के साथ काम कर रहे हों, तो शायद शून्य-लंबाई अनुक्रम वापस करना बेहतर होता है, क्योंकि यह एपीआई कार्यान्वयनकर्ता की ओर से बहुत अधिक लेखन नहीं करते हुए कम और उम्मीद से अधिक समझने योग्य कोड की ओर जाता है।
तथ्य के बाद उन्हें पता लगाने के लिए कि दूसरी कॉल शून्य थी, उन्हें दूसरी विधि से कॉल करें। ;-) अरे, यह JDBC के लिए काफी अच्छा था
इस धागे के पीछे आधार विचार रक्षात्मक रूप से कार्यक्रम करना है। यानी, अप्रत्याशित के खिलाफ कोड। विभिन्न उत्तरों की एक सरणी है:
अदाम्सकी सुझाव है कि Null Object Pattern को देखें, उस उत्तर के लिए उस सुझाव पर मतदान किया जाए।
माइकल वैलेंटी ने डेवलपर को यह बताने के लिए एक नामकरण सम्मेलन का भी सुझाव दिया कि क्या उम्मीद की जा सकती है। ZeroConcept , अपवाद का एक उचित उपयोग का सुझाव देता है, यदि वह NULL का कारण है। और दूसरे।
यदि हम "नियम" बनाते हैं जिसे हम हमेशा रक्षात्मक प्रोग्रामिंग करना चाहते हैं तो हम देख सकते हैं कि ये सुझाव मान्य हैं।
लेकिन हमारे पास 2 विकास परिदृश्य हैं।
एक डेवलपर द्वारा कक्षाएं "लेखक": लेखक
किसी अन्य (शायद) डेवलपर द्वारा कक्षाएं "भस्म": डेवलपर
भले ही कोई क्लास रिटर्न वैल्यू वाले तरीकों के लिए NULL लौटे या नहीं, डेवलपर को ऑब्जेक्ट के वैध होने पर परीक्षण करने की आवश्यकता होगी।
यदि डेवलपर ऐसा नहीं कर सकता है, तो वह वर्ग / विधि निर्धारक नहीं है। यही है, यदि ऑब्जेक्ट प्राप्त करने के लिए "विधि कॉल" वह नहीं करता है जो वह "विज्ञापन करता है" (जैसे getEmployee) ने अनुबंध को तोड़ दिया है।
एक वर्ग के लेखक के रूप में, मैं हमेशा एक विधि बनाते समय दयालु और रक्षात्मक (और निर्धारक) के रूप में बनना चाहता हूं।
इसलिए यह देखते हुए कि NULL या NULL OBJECT (जैसे कि यदि (NullEmployee.ISVALID के रूप में कर्मचारी) की जाँच की जानी चाहिए और कर्मचारियों के संग्रह के साथ ऐसा करने की आवश्यकता हो सकती है, तो अशक्त वस्तु दृष्टिकोण बेहतर दृष्टिकोण है।
लेकिन मुझे माइकल वेलेंटी के इस तरीके को नाम देने का सुझाव भी पसंद है कि MUST रिटर्न अशक्त होना चाहिए जैसे getEmployeeOrNull।
एक लेखक जो एक अपवाद को फेंकता है, डेवलपर के लिए ऑब्जेक्ट की वैधता का परीक्षण करने के लिए विकल्प को हटा रहा है, जो वस्तुओं के संग्रह पर बहुत खराब है, और डेवलपर को उनके उपभोग कोड को शाखा देते समय अपवाद से निपटने में मजबूर करता है।
एक डेवलपर वर्ग का उपभोग करने वाले के रूप में, मुझे आशा है कि लेखक मुझे अशक्त स्थिति से बचने या प्रोग्राम करने की क्षमता देता है जो उनके वर्ग / विधियों का सामना कर सकता है।
इसलिए एक डेवलपर के रूप में मैं एक विधि से NULL के खिलाफ रक्षात्मक कार्यक्रम करेगा। यदि लेखक ने मुझे एक अनुबंध दिया है जो हमेशा एक वस्तु देता है (NULL OBJECT हमेशा करता है) और उस वस्तु की एक विधि / संपत्ति होती है जिसके द्वारा वस्तु की वैधता का परीक्षण किया जाता है, तो मैं उस पद्धति / संपत्ति का उपयोग वस्तु को जारी रखने के लिए करूंगा। , अन्य वस्तु मान्य नहीं है और मैं इसका उपयोग नहीं कर सकता।
लब्बोलुआब यह है कि कक्षा / विधियों के लेखक को तंत्र प्रदान करना चाहिए जो एक डेवलपर अपनी रक्षात्मक प्रोग्रामिंग में उपयोग कर सकता है। यही है, विधि का एक स्पष्ट इरादा।
डेवलपर को हमेशा किसी अन्य वर्ग / विधि से लौटी वस्तुओं की वैधता का परीक्षण करने के लिए रक्षात्मक प्रोग्रामिंग का उपयोग करना चाहिए।
सादर
GregJF
इसके अन्य विकल्प इस प्रकार हैं: कुछ मूल्य जो सफलता को इंगित करता है या नहीं (या एक त्रुटि का प्रकार), लेकिन अगर आपको बस बूलियन मूल्य की आवश्यकता है जो सफलता / असफलता का संकेत देगा, तो विफलता के लिए अशक्त लौटना और सफलता के लिए एक वस्तु नहीं है कम सही होना, फिर सही / गलत लौटना और पैरामीटर के माध्यम से वस्तु प्राप्त करना।
अन्य दृष्टिकोण विफलताओं को इंगित करने के लिए अपवाद का उपयोग करना होगा, लेकिन यहां - वास्तव में कई और आवाजें हैं, जो कहते हैं कि यह एक बीएडी अभ्यास है (अपवादों का उपयोग करना सुविधाजनक हो सकता है लेकिन इसके कई नुकसान हैं)।
इसलिए मैं व्यक्तिगत रूप से संकेत के रूप में अशक्त लौटने में कुछ भी बुरा नहीं देखता हूं कि कुछ गलत हो गया है, और इसे बाद में जांचना (वास्तव में यह जानने के लिए कि क्या आप सफल हुए हैं या नहीं)। इसके अलावा, यह सोचकर कि आपका तरीका NULL नहीं लौटेगा, और फिर उस पर अपने कोड को आधार बना सकते हैं, अन्य को जन्म दे सकता है, कभी-कभी कठिन लगता है, त्रुटियां (हालांकि ज्यादातर मामलों में यह आपके सिस्टम को क्रैश कर देगा :), जैसा कि आप संदर्भ देंगे 0x00000000 जल्दी या बाद में)।
एक जटिल कार्यक्रमों के विकास के दौरान अनपेक्षित नल कार्य उत्पन्न हो सकते हैं, और मृत कोड की तरह, इस तरह की घटनाएं प्रोग्राम संरचनाओं में गंभीर खामियों का संकेत देती हैं।
एक नल फ़ंक्शन या विधि का उपयोग अक्सर किसी ऑब्जेक्ट फ़्रेमवर्क में किसी revectorable फ़ंक्शन या ओवरराइड करने योग्य विधि के डिफ़ॉल्ट व्यवहार के रूप में किया जाता है।
खैर, यह सुनिश्चित है कि विधि के उद्देश्य पर निर्भर करता है ... कभी-कभी, एक बेहतर विकल्प एक अपवाद फेंकना होगा। यह सब मामले से मामले पर निर्भर करता है।
यदि कोड कुछ इस तरह है:
command = get_something_to_do()
if command: # if not Null
command.execute()
यदि आपके पास एक डमी ऑब्जेक्ट है जिसका निष्पादन () विधि कुछ भी नहीं करती है, और आप इसे वापस करते हैं, तो उपयुक्त मामलों में नल के बजाय, आपको नल मामले की जांच करने की आवश्यकता नहीं है और इसके बजाय बस कर सकते हैं:
get_something_to_do().execute()
इसलिए, यहाँ समस्या NULL बनाम अपवाद की जाँच के बीच नहीं है, बल्कि इसके बजाय कॉलर के पास विशेष गैर-मामलों को अलग-अलग तरीके से (जो भी हो) या नहीं संभालना है।
मेरे उपयोग के मामले में मुझे विधि से एक मानचित्र वापस करने और फिर एक विशिष्ट कुंजी की तलाश करने की आवश्यकता थी। लेकिन अगर मैं एक खाली मानचित्र लौटाता हूं, तो यह NullPointerException को ले जाएगा और फिर यह खाली मानचित्र के बजाय बहुत अलग रिटर्निंग नल होगा। लेकिन जावा 8 से हम वैकल्पिक का उपयोग कर सकते हैं । ऊपर बहुत ही कारण है वैकल्पिक अवधारणा शुरू की गई थी।
अच्छा दिन,
जब आप एक नई वस्तु बनाने में असमर्थ होते हैं तो NULL लौटाना कई API के लिए मानक अभ्यास है।
क्यों नरक यह बुरा डिजाइन है मुझे पता नहीं है।
संपादित करें: यह उन भाषाओं का सच है जहां आपके पास सी जैसे अपवाद नहीं हैं जहां यह कई वर्षों से सम्मेलन है।
HTH
'Avahappy,