यदि किसी वस्तु के पास अमान्य स्थिति है, तो क्या एक अपवाद छोड़ देना चाहिए?


55

मैं अक्सर इस समस्या में चलता हूं, खासकर जावा में, भले ही मुझे लगता है कि यह एक सामान्य ओओपी मुद्दा है। वह यह है: एक अपवाद बढ़ाने से एक डिजाइन समस्या का पता चलता है।

मान लीजिए कि मेरे पास एक वर्ग है जिसमें एक String nameक्षेत्र और एक String surnameक्षेत्र है।

फिर यह उन क्षेत्रों का उपयोग किसी व्यक्ति का पूरा नाम लिखने के लिए करता है ताकि उसे किसी प्रकार के दस्तावेज पर प्रदर्शित किया जा सके।

public void String name;
public void String surname;

public String getCompleteName() {return name + " " + surname;}

public void displayCompleteNameOnInvoice() {
    String completeName = getCompleteName();
    //do something with it....
}

अब मैं एक त्रुटि को फेंककर अपनी कक्षा के व्यवहार को मजबूत करना चाहता हूं यदि displayCompleteNameOnInvoiceनाम निर्दिष्ट किए जाने से पहले बुलाया जाता है। यह एक अच्छा विचार है, है ना?

मैं getCompleteNameपद्धति में एक अपवाद बढ़ाने वाला कोड जोड़ सकता हूं । लेकिन इस तरह मैं कक्षा उपयोगकर्ता के साथ एक 'निहित' अनुबंध का उल्लंघन कर रहा हूं; यदि उनके मान सेट नहीं हैं, तो सामान्य गेटर्स अपवादों को फेंकना नहीं चाहते हैं। ठीक है, यह एक मानक गेट्टर नहीं है क्योंकि यह एक भी क्षेत्र नहीं लौटाता है, लेकिन उपयोगकर्ता के दृष्टिकोण से इसके बारे में सोचने के लिए भेद बहुत सूक्ष्म हो सकता है।

या मैं अंदर से अपवाद फेंक सकते हैं displayCompleteNameOnInvoice। लेकिन ऐसा करने के लिए मुझे सीधे nameया surnameखेतों का परीक्षण करना चाहिए और ऐसा करने से मैं प्रतिनिधित्व की गई अमूर्तता का उल्लंघन करूंगा getCompleteName। पूरा नाम जाँचने और बनाने के लिए यह विधि जिम्मेदारी है। यह अन्य आंकड़ों पर निर्णय को आधार बनाते हुए भी तय कर सकता है कि कुछ मामलों में यह पर्याप्त है surname

इसलिए एकमात्र संभावना यह है कि विधि के शब्दार्थ को बदल दिया getCompleteNameजाए composeCompleteName, जो एक अधिक 'सक्रिय' व्यवहार का सुझाव देता है और, इसके साथ, एक अपवाद को फेंकने की क्षमता।

क्या यह बेहतर डिजाइन समाधान है? मैं हमेशा सादगी और शुद्धता के बीच सर्वश्रेष्ठ संतुलन की तलाश में हूं। क्या इस मुद्दे के लिए एक डिज़ाइन संदर्भ है?


8
"यदि उनके मान सेट नहीं किए जाते हैं तो सामान्य रूप से अपवादों को फेंकना नहीं चाहिए।" - वे तब क्या करने वाले हैं?
रोटेम

2
@ सिस्क्रिप्टिन तो उस तर्क का पालन करते हुए, displayCompleteNameOnInvoiceयदि getCompleteNameरिटर्न देता है तो क्या वह अपवाद छोड़ nullसकता है?
रोटेम

2
@ यह कर सकते हैं। सवाल इस बारे में है कि क्या यह होना चाहिए।
स्क्रिप्ट

22
विषय पर, फाल्सहुड प्रोग्रामर्स बिलीव नेम्स के बारे में बहुत अच्छी तरह से पढ़ा है कि 'पहला नाम' और 'उपनाम' बहुत संकीर्ण अवधारणाएं क्यों हैं।
लार्स विकलुंड

9
यदि आपकी वस्तु में अमान्य स्थिति हो सकती है, तो आप पहले ही खराब कर चुके हैं।
user253751

जवाबों:


151

बिना नाम बताए अपनी कक्षा को निर्माण की अनुमति न दें।


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

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

14
@AgostinoX: यदि आवश्यक हो तो आपको केवल अपनी कक्षाओं को तरल बनाना चाहिए। अन्यथा, उन्हें यथासंभव अपरिवर्तनीय होना चाहिए।
डेडएमजी नोव

25
@gnat: आप यहाँ एक बेहतरीन काम करते हैं और PSE पर हर प्रश्न और हर उत्तर की गुणवत्ता पर सवाल उठाते हैं, लेकिन कभी-कभी आप IMHO से थोड़े नटखट भी होते हैं।
डॉक ब्राउन

9
यदि वे वैध रूप से वैकल्पिक हो सकते हैं, तो उनके पास पहले स्थान पर फेंकने का कोई कारण नहीं है।
डेडएमजी नोव

75

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

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


7
So if you want fluidity, use something like the builder pattern- तरलता, या अपरिवर्तनीयता (जब तक कि आपका मतलब किसी तरह से न हो)। यदि कोई अपरिवर्तनीय वस्तु बनाना चाहता है, लेकिन कुछ मूल्यों को निर्धारित करने की आवश्यकता है, तो पालन करने के लिए पैटर्न उन्हें एक बिल्डर ऑब्जेक्ट पर एक-एक करके सेट करना है और केवल एक अपरिवर्तनीय बनाना है, जब हमने सही के लिए आवश्यक सभी मान एकत्र किए हैं राज्य ...
कोनराड मोरावस्की

1
@KonradMorawski: मैं भ्रमित हूं। क्या आप मेरे कथन को स्पष्ट कर रहे हैं या विरोधाभासी हैं? ;)
बैक

3
मैंने इसे पूरक करने की कोशिश की ...
कोनराड मोरावस्की

40

अमान्य राज्य के पास कोई ऑब्जेक्ट नहीं है।

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

तो, इस भाग के लिए सवाल यह है कि "आप नाम या उपनाम को शून्य क्यों होने दे रहे हैं?" यदि वह ऐसा कुछ नहीं है जो कक्षा में ठीक से काम करने के लिए मान्य है, तो उसे अनुमति न दें। एक निर्माता या बिल्डर है जो ऑब्जेक्ट के निर्माण को ठीक से सत्यापित करता है और यदि यह सही नहीं है, तो उस बिंदु पर समस्या उठाएं। आप यह भी कह सकते हैं कि "यह शून्य नहीं हो सकता" संवाद करने और कोडिंग और विश्लेषण में इसे लागू करने में मदद करने के लिए मौजूद @NotNullएनोटेशन में से एक का उपयोग करें ।

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

अधिक प्राप्त करने वाले।

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

इस के मूल से आता है:

यदि उनके मान सेट नहीं हैं, तो सामान्य गेटर्स अपवादों को फेंकने के लिए नहीं हैं

और आप सही हैं। उन्हें नहीं करना चाहिए उन्हें दुनिया के बाकी हिस्सों को 'गूंगा' दिखना चाहिए और वही करना चाहिए जो अपेक्षित है। वहां बहुत अधिक तर्क रखने से उन मुद्दों की ओर बढ़ जाता है जहां कम से कम विस्मय के कानून का उल्लंघन होता है। और इसका सामना करने देता है, आप वास्तव में गेटर्स को लपेटना नहीं चाहते हैं try catchक्योंकि हम जानते हैं कि हम सामान्य रूप से कितना प्यार करते हैं।

ऐसी स्थितियाँ भी हैं जहाँ आपको एक विधि का उपयोग करना चाहिएgetFoo जैसे कि जावाबीन फ्रेमवर्क और जब आपके पास ईएल कॉलिंग से कुछ हो तो बीन प्राप्त करने की अपेक्षा करें (ताकि <% bar.foo %>वास्तव getFoo()में क्लास में कॉल करें - एक तरफ स्थापित करना 'बीन को कंपोज़िशन करना चाहिए या करना चाहिए' यह देखने के लिए छोड़ दिया जाना चाहिए? 'क्योंकि एक आसानी से उन स्थितियों के साथ आ सकता है जहां एक या दूसरा स्पष्ट रूप से सही उत्तर हो सकता है)

यह भी महसूस करें कि किसी दिए गए गेटटर के लिए एक इंटरफ़ेस में निर्दिष्ट किया जाना संभव है या उस वर्ग के लिए पहले से उजागर सार्वजनिक एपीआई का हिस्सा रहा है जो कि रिफैक्ट हो रहा है (एक पिछला संस्करण सिर्फ 'पूरा नाम' था जो वापस आ गया था और एक रिफैक्ट्री टूट गया था यह दो क्षेत्रों में) है।

आखिरकार दिन के अंत में...

वह काम करें जिसके बारे में तर्क करना सबसे आसान है। आप इस कोड को बनाए रखने में अधिक समय बिताएंगे, जबकि आप इसे डिजाइन करने में खर्च करेंगे (हालांकि आप डिजाइनिंग में जितना कम समय बिताएंगे, उतना ही समय आप बनाए रखने में खर्च करेंगे)। आदर्श विकल्प इस तरह से है कि यह के रूप में ज्यादा समय बनाए रखने के लिए नहीं ले जाएगा में यह डिजाइन करने के लिए, लेकिन वहाँ दिनों के लिए इस बारे में सोच बैठते नहीं है या तो - जब तक कि यह वास्तव में एक डिजाइन पसंद वह यह है कि होगा निहितार्थ के दिनों के बाद है ।

private T foo; T getFoo() { return foo; }पाने वाले की शुद्धता के साथ चिपके रहने की कोशिश आपको किसी समय परेशानी में डाल देगी। कभी-कभी कोड केवल उस मॉडल और उन अंतर्विरोधों के लिए फिट नहीं होता है, जो किसी के माध्यम से जाता है वह इसे रखने की कोशिश करता है जिस तरह से इसका कोई मतलब नहीं है ... और अंततः इसे डिजाइन करना कठिन हो जाता है।

कभी-कभी स्वीकार करें कि डिज़ाइन के आदर्शों को उस तरह से महसूस नहीं किया जा सकता है जिस तरह से आप उन्हें कोड में चाहते हैं। दिशानिर्देश दिशानिर्देश हैं - झोंपड़ी नहीं।


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

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

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

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

5
"उस काम को करें जो सबसे आसान है।" यही कारण है कि
बेन

5

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

getCompleteNameयदि नाम असिंचित हैं, और अपवाद नहीं displayCompleteNameOnInvoiceकरता है।

public String getCompleteName()
{
    if (name == null || surname == null)
    {
        return null;
    }
    return name + " " + surname;
}

public void displayCompleteNameOnInvoice() 
{
    String completeName = getCompleteName();
    if (completeName == null)
    {
        //throw an exception.
    }
    //do something with it....
}

यह सही समाधान क्यों है इसका विस्तार करने के लिए: इस तरह से कॉल करने वाले को getCompleteName()यह ध्यान रखने की आवश्यकता नहीं है कि संपत्ति वास्तव में संग्रहीत है या यदि यह मक्खी पर गणना की जाती है।
बार्ट वैन इनगेन शेनॉ नोव

18
इस समाधान का विस्तार करने के लिए कि यह बैशिट पागल क्यों है, आपको हर एक कॉल पर अशक्तता की जांच करनी होगी getCompleteName, जो कि गुप्त है।
डेडजैम

नहीं, @DeadMG, आपको केवल उन मामलों में इसकी जांच करनी होगी जहां getCompleteName रिटर्न शून्य होने पर अपवाद छोड़ दिया जाना चाहिए। यह किस तरह से होना चाहिए। यह समाधान सही है।
दाऊद इब्न करीम

1
@ डीडीएमजी हां, लेकिन हम यह नहीं जानते कि अन्य उपयोग क्या हैं getCompleteName()बाकी कक्षा में, या यहां तक ​​कि कार्यक्रम के अन्य हिस्सों में भी। कुछ मामलों में, वापसी nullवही हो सकती है जिसकी आवश्यकता है। लेकिन जब एक तरीका है जिसकी कल्पना "इस मामले में एक अपवाद फेंक" है, तो यह बिल्कुल वही है जिसे हमें कोड करना चाहिए।
दाऊद इब्न करीम

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

4

ऐसा लगता है जैसे कोई भी आपके सवाल का जवाब नहीं दे रहा है।
लोगों का मानना ​​है कि इसके विपरीत, "अमान्य वस्तुओं से बचें" अक्सर एक व्यावहारिक समाधान नहीं है।

उत्तर है:

हाँ, यह चाहिए।

आसान उदाहरण: FileStream.LengthC # /। NET में , जो NotSupportedExceptionकि फाइल की तलाश करने योग्य नहीं होने पर फेंकता है।


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
maple_shaft

1

आपके पास पूरी जाँच नाम की चीज़ उल्टी है।

getCompleteName()यह जानने की जरूरत नहीं है कि कौन से कार्य इसका उपयोग करेंगे। इस प्रकार, nameजब कॉलिंग displayCompleteNameOnInvoice()नहीं होती है तो getCompleteName()जिम्मेदारी नहीं होती है ।

लेकिन, के nameलिए एक स्पष्ट शर्त है displayCompleteNameOnInvoice()। इस प्रकार, इसे राज्य की जांच करने की ज़िम्मेदारी लेनी चाहिए (या यदि आप चाहें, तो इसे किसी अन्य विधि से जाँचने की ज़िम्मेदारी सौंपनी चाहिए )।

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