प्रकार परीक्षण कब ठीक है?


53

कुछ अंतर्निहित प्रकार की सुरक्षा के साथ एक भाषा मान लेना (जैसे, जावास्क्रिप्ट नहीं):

ऐसी पद्धति को देखते हुए SuperType, जिसे हम स्वीकार करते हैं , हम जानते हैं कि ज्यादातर मामलों में जिसमें हमें कार्रवाई करने के लिए टाइप परीक्षण करने के लिए लुभाया जा सकता है:

public void DoSomethingTo(SuperType o) {
  if (o isa SubTypeA) {
    o.doSomethingA()
  } else {
    o.doSomethingB();
  }
}

हमें आमतौर पर, यदि हमेशा नहीं होता है, SuperTypeतो इस पर एक एकल, अधिक उपयोगी विधि बनाएं और ऐसा करें:

public void DoSomethingTo(SuperType o) {
  o.doSomething();
}

... जिसमें प्रत्येक उपप्रकार को अपना doSomething()कार्यान्वयन दिया जाता है । हमारे आवेदन के बाकी तब उचित रूप से अनभिज्ञ हो सकते हैं कि क्या कोई दिया गया SuperTypeवास्तव में एक है SubTypeAया नहीं SubTypeB

आश्चर्यजनक।

लेकिन, हम अभी भी is aअधिकांश में समान संचालन दे रहे हैं, यदि सभी नहीं, तो टाइप-सुरक्षित भाषाएं। और यह स्पष्ट प्रकार के परीक्षण के लिए संभावित आवश्यकता का सुझाव देता है।

तो, क्या में स्थितियों, यदि कोई हो, चाहिए हम या चाहिए हम स्पष्ट प्रकार परीक्षण?

मेरी अनुपस्थित मानसिकता या रचनात्मकता की कमी को क्षमा करें। मुझे पता है कि मैंने इसे पहले किया है; लेकिन, यह ईमानदारी से बहुत समय पहले मुझे याद नहीं था कि मैंने जो किया वह अच्छा था! और हाल की स्मृति में, मुझे नहीं लगता कि मुझे अपने चरवाहे जावास्क्रिप्ट के बाहर के प्रकारों का परीक्षण करने की आवश्यकता है।



4
यह इंगित करने योग्य है कि न तो जावा और न ही सी # में उनके पहले संस्करण में जेनेरिक थे। आपको कंटेनर का उपयोग करने के लिए और ऑब्जेक्ट से कास्टिंग करना था।
डोभाल

6
"टाइप चेकिंग" लगभग हमेशा जाँच करने के लिए संदर्भित करता है कि क्या कोड भाषा के स्थिर टाइपिंग नियमों का पालन करता है। आपके कहने का मतलब आमतौर पर टाइप टेस्टिंग है


3
यही कारण है कि मुझे C ++ लिखना और RTTI बंद करना पसंद है। जब कोई वस्तुतः रनटाइम पर ऑब्जेक्ट प्रकारों का परीक्षण नहीं कर सकता है, तो यह डेवलपर को यहां पूछे जाने वाले प्रश्न के संबंध में अच्छे ओओ डिजाइन का पालन करने के लिए मजबूर करता है।

जवाबों:


48

"कभी नहीं" विहित उत्तर है "जब प्रकार परीक्षण ठीक है?" इसे साबित करने या उसे अस्वीकार करने का कोई तरीका नहीं है; यह "अच्छा डिज़ाइन" या "अच्छा ऑब्जेक्ट-ओरिएंटेड डिज़ाइन" के बारे में मान्यताओं की एक प्रणाली का हिस्सा है। यह भी खोखला है।

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

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

समाधान: मुहावरे का प्रयोग करें। जैसा कि आपने सुझाव दिया था, प्रत्येक वर्ग के लिए एक विधि जोड़ें, फिर मानक विरासत और विधि-चयन एल्गोरिदम निर्धारित करें कि कौन सा मामला लागू होता है। या यदि आप आधार प्रकार नहीं बदल सकते हैं, तो उपवर्ग और वहां अपना तरीका जोड़ें।

इतना पारंपरिक ज्ञान के लिए, और कुछ जवाब के लिए। कुछ मामलों में जहां स्पष्ट प्रकार का परीक्षण समझ में आता है:

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

  2. आप कक्षाओं को समायोजित नहीं कर सकते। आप उपवर्ग के बारे में सोचते हैं - लेकिन आप नहीं कर सकते। उदाहरण के लिए, जावा में कई कक्षाएं निर्दिष्ट हैं final। आप फेंकने की कोशिश करते हैं public class ExtendedSubTypeA extends SubTypeA {...} और संकलक आपको बिना किसी अनिश्चित शब्दों के बताता है कि आप जो कर रहे हैं वह संभव नहीं है। क्षमा, अनुग्रह और वस्तु उन्मुख मॉडल का परिष्कार! किसी ने तय किया कि आप उनके प्रकारों का विस्तार नहीं कर सकते हैं! दुर्भाग्य से, कई मानक पुस्तकालय हैं final, और कक्षाएं बनाना finalसामान्य डिजाइन मार्गदर्शन है। एक फंक्शन एंड-रन आपके लिए उपलब्ध है।

    BTW, यह सांख्यिकीय रूप से टाइप की गई भाषाओं तक सीमित नहीं है। डायनेमिक भाषा पायथन में कई बेस क्लास हैं, जो C में लागू किए गए कवर के तहत वास्तव में संशोधित नहीं किए जा सकते हैं। जावा की तरह, जिसमें अधिकांश मानक प्रकार शामिल हैं।

  3. आपका कोड बाहरी है। आप उन कक्षाओं और ऑब्जेक्ट्स के साथ विकसित हो रहे हैं जो डेटाबेस सर्वर, मिडलवेयर इंजन और अन्य कोडबेस से आते हैं जिन्हें आप नियंत्रित या समायोजित नहीं कर सकते हैं। आपका कोड कहीं और उत्पन्न वस्तुओं का एक कम उपभोक्ता है। यहां तक ​​कि अगर आप उपवर्ग कर सकते हैं SuperType, तो आप उन पुस्तकालयों को प्राप्त करने में सक्षम नहीं होंगे, जिन पर आप अपने उपवर्गों में वस्तुओं को उत्पन्न करना निर्भर करते हैं। वे आपको उन प्रकारों के उदाहरणों को सौंपने जा रहे हैं जो वे जानते हैं, आपके वेरिएंट नहीं। यह हमेशा ऐसा नहीं होता है ... कभी-कभी वे लचीलेपन के लिए बनाए जाते हैं, और वे गतिशील रूप से उन कक्षाओं के इंस्टेंसेस को इंस्टेंट करते हैं जो आप उन्हें खिलाते हैं। या वे उन उपवर्गों को पंजीकृत करने के लिए एक तंत्र प्रदान करते हैं जिन्हें आप अपने कारखानों का निर्माण करना चाहते हैं। XML पार्सर ऐसे प्रवेश बिंदु प्रदान करने में विशेष रूप से अच्छे लगते हैं; उदाहरण देखें या पायथन में lxml । लेकिन अधिकांश कोड आधार ऐसे एक्सटेंशन प्रदान नहीं करते हैं। वे आपको उन कक्षाओं को वापस सौंपने जा रहे हैं जिन्हें वे बनाया गया था और जिनके बारे में जानते थे। यह आम तौर पर आपके कस्टम परिणामों में उनके परिणामों को प्रॉक्सी करने के लिए समझ में नहीं आएगा ताकि आप एक शुद्ध वस्तु-उन्मुख प्रकार चयनकर्ता का उपयोग कर सकें। यदि आप टाइप भेदभाव करने जा रहे हैं, तो आप इसे अपेक्षाकृत गंभीर रूप से करने जा रहे हैं। आपका प्रकार परीक्षण कोड तब काफी उपयुक्त लगता है।

  4. गरीब व्यक्ति की जेनेरिक / कई प्रेषण। आप अपने कोड के लिए विभिन्न प्रकार के विभिन्न प्रकार स्वीकार करना चाहते हैं, और महसूस करते हैं कि बहुत प्रकार के विशिष्ट तरीकों की एक सरणी सुशोभित नहीं है। public void add(Object x)तार्किक लगता है, लेकिन की एक सरणी नहीं addByte, addShort, addInt, addLong, addFloat, addDouble, addBoolean, addChar, और addStringवेरिएंट (कुछ नाम हैं)। ऐसे कार्य या विधियाँ जो एक उच्च सुपर-प्रकार लेते हैं और फिर निर्धारित करते हैं कि किस प्रकार के आधार पर क्या करना है - वे आपको वार्षिक बूच-लिस्कोव डिज़ाइन संगोष्ठी में पवित्रता पुरस्कार जीतने नहीं जा रहे हैं, लेकिन छोड़ने हंगेरियन नामकरण आपको एक सरल एपीआई देगा। एक अर्थ में, आपका is-aयाis-instance-of परीक्षण एक भाषा के संदर्भ में सामान्य या बहु-प्रेषण का अनुकरण कर रहा है जो मूल रूप से इसका समर्थन नहीं करता है।

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

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


यदि किसी विशेष प्रकार पर एक ऑपरेशन नहीं किया जा सकता है, लेकिन दो अलग-अलग प्रकारों पर प्रदर्शन किया जा सकता है, जिसमें यह रूपांतरित परिवर्तनीय होगा, ओवरलोडिंग केवल तभी उपयुक्त है जब दोनों रूपांतरण समान परिणाम प्राप्त करेंगे। अन्यथा एक। NET में जैसे क्रमी व्यवहार के साथ समाप्त होता है: (1.0).Equals(1.0f)सही [तर्क को बढ़ावा देता है double], लेकिन (1.0f).Equals(1.0)झूठे [तर्क को बढ़ावा देता है object]; जावा में, Math.round(123456789*1.0)123456789 Math.round(123456789*1.0)पैदावार देता है , लेकिन 123456792 पैदावार करता है [तर्क floatइसके बजाय बढ़ावा देता है double]।
सुपरकैट

यह स्वचालित प्रकार की कास्टिंग / वृद्धि के खिलाफ क्लासिक तर्क है। खराब और विरोधाभासी परिणाम, कम से कम किनारे के मामलों में। मैं सहमत हूं, लेकिन यह सुनिश्चित नहीं है कि आप इसे मेरे उत्तर से कैसे संबंधित हैं।
जोनाथन यूनिस

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

2
@ सुपरफ़ास्ट ने मेरी ख़राब नज़र को दोष दिया, लेकिन मेरे साथ दो भाव Math.roundसमान दिखते हैं। क्या फर्क पड़ता है?
लिली चुंग

2
@IstvanChung: उफ़्फ़ ... बाद वाला होना चाहिए था Math.round(123456789)[अगर किसी Math.round(thing.getPosition() * COUNTS_PER_MIL)को एक अनियंत्रित स्थिति मान वापस करने के लिए लिखा जाए तो क्या हो सकता है , इस बात का अहसास नहीं है कि getPositionएक रिटर्न देता है intया नहीं long।]
सुपरकैट

25

मुख्य परिस्थिति जिसकी मुझे कभी आवश्यकता होती है, वह थी दो वस्तुओं की तुलना करना, जैसे कि एक equals(other)विधि, जिसमें सटीक प्रकार के आधार पर अलग-अलग एल्गोरिदम की आवश्यकता हो सकती है other। फिर भी, यह काफी दुर्लभ है।

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

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


1
मैं क्षणभंगुर मामले से बहुत खुश था, वहाँ इसका इस्तेमाल करते हुए झूठा याद आया; लेकिन, अब मैं सोच भी नहीं सकता कि मैं कैसा होता! मुझे पता है कि मैंने उसके लिए कुछ अजीब प्रकार के लुकअप किए हैं; लेकिन मुझे यकीन नहीं है कि यह प्रकार परीक्षण का गठन करता है या नहीं । हो सकता है कि धारावाहिक बनाना एक समानांतर समानांतर है: अपने ठोस प्रकार के लिए वस्तुओं से पूछताछ करना।
svidgen

1
आमतौर पर बहुरूपता का उपयोग करके सीरियल करना संभव है। डीसर्विलाइज़ करते समय, आप अक्सर कुछ ऐसा करते हैं BaseClass base = deserialize(input), क्योंकि आप अभी तक प्रकार नहीं जानते हैं, तो आप if (base instanceof Derived) derived = (Derived)baseइसे अपने सटीक व्युत्पन्न प्रकार के रूप में संग्रहीत करते हैं।
कार्ल बेवेलफेल्ट

1
समानता समझ में आती है। मेरे अनुभव में, इस तरह के तरीकों का अक्सर एक रूप होता है जैसे "यदि इन दो वस्तुओं का एक ही ठोस प्रकार है, तो वापस लौटें कि क्या उनके सभी क्षेत्र समान हैं; अन्यथा, गलत (या अयोग्य) लौटें।
जॉन पर्पडी

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

12

मानक (लेकिन उम्मीद से दुर्लभ) मामला इस तरह दिखता है: यदि निम्न स्थिति में

public void DoSomethingTo(SuperType o) {
  if (o isa SubTypeA) {
    DoSomethingA((SubTypeA) o )
  } else {
    DoSomethingB((SubTypeB) o );
  }
}

कार्यों DoSomethingAया DoSomethingBआसानी से की विरासत के पेड़ के सदस्य कार्यों के रूप में लागू नहीं किया जा सकता SuperType/ SubTypeA/ SubTypeB। उदाहरण के लिए, यदि

  • उपप्रकार एक पुस्तकालय का हिस्सा है जिसे आप बदल नहीं सकते हैं, या
  • अगर DoSomethingXXXउस लाइब्रेरी के लिए कोड जोड़ना एक निषिद्ध निर्भरता शुरू करने का मतलब होगा।

ध्यान दें कि अक्सर ऐसी परिस्थितियाँ होती हैं, जहाँ आप इस समस्या को दरकिनार कर सकते हैं (उदाहरण के लिए, एक रैपर या एडेप्टर बनाकर , SubTypeAऔर SubTypeB, या इसके DoSomethingमूल संचालन के संदर्भ में पूरी तरह से फिर से लागू करने की कोशिश कर रहे हैं SuperType), लेकिन कभी-कभी ये समाधान परेशानी या बनाने के लायक नहीं होते हैं स्पष्ट प्रकार का परीक्षण करने की तुलना में चीजें अधिक जटिल और कम एक्स्टेंसिबल हैं।

मेरे कल के काम का एक उदाहरण: मेरे पास एक ऐसी स्थिति थी जहां मैं वस्तुओं की सूची के प्रसंस्करण को समानांतर करने जा रहा था (प्रकार की SuperType, बिल्कुल दो अलग-अलग उपप्रकारों के साथ, जहां यह बहुत संभावना नहीं है कि कभी भी अधिक होगा)। अनपेक्षित समानांतर संस्करण में दो लूप होते हैं: उपप्रकार ए की वस्तुओं के लिए एक लूप, कॉलिंग DoSomethingA, और उपप्रकार बी की वस्तुओं के लिए एक दूसरा लूप, कॉलिंग DoSomethingB

"DoSomethingA" और "DoSomethingB" विधियाँ दोनों समय की गहन गणनाएँ हैं, संदर्भ जानकारी का उपयोग करते हुए जो उप-वर्गों ए और बी के दायरे में उपलब्ध नहीं है (इसलिए उन्हें उपप्रकारों के सदस्य कार्यों के रूप में लागू करने का कोई मतलब नहीं है)। नए "समानांतर लूप" के दृष्टिकोण से, यह समान रूप से उनके साथ काम करके चीजों को बहुत आसान बनाता है, इसलिए मैंने DoSomethingToऊपर से समान फ़ंक्शन लागू किया । हालाँकि, "DoSomethingA" और "DoSomethingB" के कार्यान्वयन में देखने से पता चलता है कि वे आंतरिक रूप से बहुत अलग तरीके से काम करते हैं। इसलिए SuperTypeबहुत सारे अमूर्त तरीकों के साथ विस्तार करके एक सामान्य "Doomomething" को लागू करने की कोशिश करना वास्तव में काम नहीं करने वाला था, या पूरी तरह से चीजों को ओवरडिजाइन करने का मतलब होगा।


2
कोई भी मौका जो आप मेरे धूमिल मस्तिष्क को समझाने के लिए एक छोटा, ठोस उदाहरण जोड़ सकते हैं, इस परिदृश्य से वंचित नहीं हैं?
svidgen

स्पष्ट रूप से, मैं यह नहीं कह रहा हूँ यह है काल्पनिक। मुझे संदेह है कि मैं आज केवल अति-धूमिल दिमाग वाला हूं।
svidgen

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

मुझे लगता है कि आपका संपादन, जिसकी मैंने अनदेखी की थी, एक अच्छा उदाहरण देता है। ... क्या ऐसे उदाहरण हैं जिनमें यह तब भी ठीक है जब आप नियंत्रण करते हैं SuperTypeऔर यह उपवर्ग है?
svidgen

6
यहाँ एक ठोस उदाहरण है: एक वेब सेवा से JSON प्रतिक्रिया में शीर्ष-स्तरीय कंटेनर या तो एक शब्दकोश या एक सरणी हो सकता है। आमतौर पर, आपके पास कुछ उपकरण होते हैं जो JSON को वास्तविक ऑब्जेक्ट्स (जैसे NSJSONSerializationओब्ज-सी) में बदल देते हैं, लेकिन आप केवल यह भरोसा नहीं करना चाहते हैं कि प्रतिक्रिया में वह प्रकार शामिल है जिसकी आप अपेक्षा करते हैं, इसलिए इसका उपयोग करने से पहले आप इसे जांच लें (जैसे if ([theResponse isKindOfClass:[NSArray class]])...) ।
कालेब

5

जैसा कि अंकल बॉब इसे कहते हैं:

When your compiler forgets about the type.

अपने एक क्लीन कोडर एपिसोड में उन्होंने एक फ़ंक्शन कॉल का एक उदाहरण दिया, जिसका उपयोग Employeeएस वापस करने के लिए किया जाता है । Managerका एक उप-प्रकार है Employee। मान लेते हैं कि हमारे पास एक एप्लिकेशन सेवा है जो एक Managersumm आईडी को स्वीकार करती है और उसे कार्यालय में बुलाती है :) फ़ंक्शन getEmployeeById()एक सुपर-प्रकार लौटाता है Employee, लेकिन मैं जांचना चाहता हूं कि क्या प्रबंधक इस उपयोग-मामले में वापस आ गया है।

उदाहरण के लिए:

var manager = employeeRepository.getEmployeeById(empId);
if (!(manager is Manager))
   throw new Exception("Invalid Id specified.");
manager.summon();

यहां मैं यह देखने के लिए जांच कर रहा हूं कि क्या क्वेरी द्वारा लौटाया गया कर्मचारी वास्तव में एक प्रबंधक है (यानी मुझे उम्मीद है कि यह एक प्रबंधक होगा और यदि अन्यथा तेजी से विफल हो जाता है)।

सबसे अच्छा उदाहरण नहीं है, लेकिन यह सब के बाद चाचा बॉब है।

अपडेट करें

मैंने उदाहरण को उतना ही अपडेट किया जितना मैं मेमोरी से याद रख सकता हूं।


1
सिर्फ इस उदाहरण में अपवाद को Managerलागू करना क्यों नहीं है summon()?
svidgen

@ एसवीडजेन शायद एस CEOको तलब कर सकता है Manager
user253751

@ एसवीडजेन, तब यह स्पष्ट नहीं होगा कि कर्मचारीरिपोसेरिटरी.गेट इम्प्लॉईबाइ (एम्पिड) से एक प्रबंधक के लौटने की उम्मीद है
इयान

@ मैं एक समस्या के रूप में नहीं देख सकता। यदि कॉलिंग कोड एक के लिए पूछ रहा है Employee, तो उसे केवल यह ध्यान रखना चाहिए कि उसे कुछ ऐसा मिलता है जो एक की तरह व्यवहार करता है Employee। अगर अलग-अलग उपवर्गों के Employeeपास अलग-अलग अनुमतियां, जिम्मेदारियां आदि हैं, तो वास्तविक अनुमतियों की प्रणाली की तुलना में टाइप-टेस्टिंग बेहतर विकल्प क्या है?
svidgen

3

टाइप चेक करना कब ठीक है?

कभी नहीँ।

  1. किसी अन्य फ़ंक्शन में उस प्रकार से संबंधित व्यवहार होने से, आप ओपन बंद सिद्धांत का उल्लंघन कर रहे हैं , क्योंकि आप isक्लॉज़ को बदलकर , या कुछ भाषाओं में, या व्यवहार के आधार पर टाइप के मौजूदा व्यवहार को संशोधित करने के लिए स्वतंत्र हैं परिदृश्य) क्योंकि आप isचेक को करने वाले फ़ंक्शन के आंतरिक को संशोधित किए बिना प्रकार का विस्तार नहीं कर सकते।
  2. इससे भी महत्वपूर्ण बात isयह है कि चेक एक मजबूत संकेत है कि आप लिस्कोव प्रतिस्थापन सिद्धांत का उल्लंघन कर रहे हैं । जो कुछ भी काम करता है SuperTypeवह पूरी तरह से अनभिज्ञ होना चाहिए कि क्या उप प्रकार हो सकते हैं।
  3. आप कर रहे हैं परोक्ष प्रकार के नाम के साथ कुछ व्यवहार जोड़। यह आपके कोड को बनाए रखने के लिए कठिन बनाता है क्योंकि ये निहित अनुबंध पूरे कोड में फैले हुए हैं और सार्वभौमिक रूप से लागू होने की गारंटी नहीं है और वास्तविक वर्ग की तरह लागू होते हैं।

सभी ने कहा, isचेक अन्य विकल्पों की तुलना में कम खराब हो सकते हैं । एक बेस क्लास में सभी सामान्य कार्यक्षमता को रखना भारी-भरकम होता है और अक्सर इससे भी बदतर समस्याएं पैदा होती हैं। एक एकल वर्ग का उपयोग करना, जिसमें "प्रकार" के लिए एक ध्वज या एनम है उदाहरण है ... भयानक से भी बदतर है, क्योंकि अब आप सभी उपभोक्ताओं के लिए टाइप सिस्टम परिधि फैला रहे हैं ।

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


3
एक मामूली चेतावनी है: उन भाषाओं में बीजीय डेटा प्रकारों को लागू करना जो उनका समर्थन नहीं करते हैं। हालाँकि, इनहेरिटेंस और टाइपेकिटिंग का उपयोग विशुद्ध रूप से एक हैकी कार्यान्वयन विवरण है; अभिप्राय उपप्रकारों को प्रस्तुत करना नहीं है, बल्कि मूल्यों को वर्गीकृत करना है। मैं इसे केवल इसलिए उठाता हूं क्योंकि ADT उपयोगी हैं और कभी भी बहुत मजबूत क्वालीफायर नहीं है, लेकिन अन्यथा मैं पूरी तरह से सहमत हूं; instanceofकार्यान्वयन विवरण लीक करता है और अमूर्तता को तोड़ता है।
डोभाल

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

10
यदि "कभी नहीं" से आपका वास्तव में मतलब है "कभी-कभी," तो आप सही हैं।
कालेब

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

2
@ सुपरकैट: एक विधि को कम से कम विशिष्ट प्रकार की मांग करनी चाहिए जो इसकी आवश्यकताओं को पूरा करती हैIEnumerable<T>एक "अंतिम" तत्व का वादा नहीं करता है। यदि आपके विधि को इस तरह के तत्व की आवश्यकता है, तो इसे एक प्रकार की आवश्यकता होनी चाहिए जो किसी के अस्तित्व की गारंटी देता है। और उस प्रकार के उपप्रकार "अंतिम" पद्धति के कुशल कार्यान्वयन प्रदान कर सकते हैं।
cHao

2

यदि आपको एक बड़ा कोड आधार (कोड के 100K से अधिक) मिला है और वे शिपिंग के करीब हैं या एक शाखा में काम कर रहे हैं जिसे बाद में मर्ज करना होगा, और इसलिए बहुत से सामना करने के लिए बहुत अधिक लागत / जोखिम है।

आपके पास कभी-कभी सिस्टम के एक बड़े अपवर्तक या कुछ सरल स्थानीयकृत "प्रकार परीक्षण" का विकल्प होता है। यह एक तकनीकी ऋण बनाता है जिसे जल्द से जल्द वापस भुगतान किया जाना चाहिए, लेकिन अक्सर ऐसा नहीं होता है।

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

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


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

यूआई के किस संस्करण को दिखाना है, यह तय करने के लिए आप "टाइप टेस्टिंग" का उपयोग कर सकते हैं या कुछ फैंसी लुकअप टेबल रख सकते हैं जो "डोमेन क्लासेस" से "यूआई कक्षाओं" में परिवर्तित हो जाती हैं। लुकअप टेबल एक तरह से "टाइप टेस्टिंग" को छिपाने का एक तरीका है।

(डेटाबेस अपडेटिंग कोड में यूआई कोड के समान मुद्दे हो सकते हैं, हालांकि आप केवल डेटाबेस अपडेटिंग कोड के एक सेट के लिए हैं, लेकिन आपके पास बहुत सारी अलग-अलग स्क्रीन हो सकती हैं जो दिखाए जाने वाले ऑब्जेक्ट के प्रकार के अनुकूल होना चाहिए।)


विज़िटर पैटर्न अक्सर आपके UI केस को हल करने का एक अच्छा तरीका है।
इयान गोल्डबी

@IanGoldby, कई बार यह सहमत हो सकता है, हालांकि आप अभी भी "टाइप परीक्षण" कर रहे हैं, बस थोड़ा सा छिपा हुआ है।
इयान

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

@IanGoldby, मेरा मतलब इस अर्थ में छिपा है कि यह WPF या WinForms कोड को समझने में कठिन बना सकता है। मुझे उम्मीद है कि कुछ वेब बेस UI के लिए यह बहुत अच्छा काम करेगा।
इयान

2

LINQ का कार्यान्वयन संभावित प्रदर्शन अनुकूलन के लिए कई प्रकार की जाँच का उपयोग करता है, और फिर IEnumerable के लिए एक वापसी।

सबसे स्पष्ट उदाहरण संभवतः एलीमेंट विधि (.NET 4.5 स्रोत का छोटा अंश) है:

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, int index) { 
    IList<TSource> list = source as IList<TSource>;

    if (list != null) return list[index];
    // ... and then an enumerator is created and MoveNext is called index times

लेकिन Enumerable वर्ग में बहुत सारे स्थान हैं जहां एक समान पैटर्न का उपयोग किया जाता है।

इसलिए शायद आमतौर पर उपयोग किए जाने वाले उपप्रकार के प्रदर्शन के लिए अनुकूलन एक वैध उपयोग है। मुझे यकीन नहीं है कि यह कैसे बेहतर तरीके से डिजाइन किया जा सकता था।


यह एक सुविधाजनक साधन प्रदान करके बेहतर तरीके से डिज़ाइन किया जा सकता था, जिसके द्वारा इंटरफेस डिफ़ॉल्ट कार्यान्वयन की आपूर्ति कर सकते हैं, और फिर IEnumerable<T>उन जैसे कई तरीकों को शामिल कर सकते हैं List<T>, एक Featuresसंपत्ति के साथ संकेत दे सकते हैं कि किन तरीकों से अच्छी तरह से, धीरे-धीरे काम करने की उम्मीद की जा सकती है या नहीं, साथ ही विभिन्न मान्यताओं के बारे में एक उपभोक्ता सुरक्षित रूप से संग्रह के बारे में बना सकता है (जैसे कि इसका आकार और / या इसकी मौजूदा सामग्री कभी नहीं बदलने की गारंटी देती है [एक प्रकार का समर्थन कर सकता है Addजबकि यह गारंटी देता है कि मौजूदा सामग्री अपरिवर्तनीय होगी])।
सुपरकैट

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

1

एक उदाहरण है जो अक्सर गेम के विकास में सामने आता है, विशेष रूप से टक्कर का पता लगाने में, जो कि कुछ प्रकार के परीक्षण के उपयोग के बिना निपटना मुश्किल है।

मान लें कि सभी खेल ऑब्जेक्ट एक सामान्य आधार वर्ग से प्राप्त होते हैं GameObject। प्रत्येक वस्तु एक कठोर शरीर टक्कर आकार CollisionShapeजो एक आम इंटरफेस उपलब्ध करा सकता है, लेकिन वास्तविक टक्कर आकार सभी तरह के रूप में ठोस उपवर्गों हो जाएगा (क्वेरी स्थिति, अभिविन्यास, आदि कहने के लिए) Sphere, Box, ConvexHull, आदि के भंडारण के ज्यामितीय वस्तु के प्रकार के लिए विशिष्ट जानकारी ( वास्तविक उदाहरण के लिए यहां देखें )

अब, टकराव के लिए परीक्षण करने के लिए मुझे प्रत्येक जोड़ी को टक्कर आकार के प्रकारों के लिए एक फ़ंक्शन लिखना होगा:

detectCollision(Sphere, Sphere)
detectCollision(Sphere, Box)
detectCollision(Sphere, ConvexHull)
detectCollision(Box, ConvexHull)
...

उन दो ज्यामितीय प्रकारों के प्रतिच्छेदन को करने के लिए आवश्यक विशिष्ट गणित समाहित करता है।

मेरे गेम लूप के प्रत्येक 'टिक' पर मुझे टकराव के लिए वस्तुओं के जोड़े की जांच करने की आवश्यकता है। लेकिन मेरे पास केवल GameObjects और उनकी संगत CollisionShapes तक पहुंच है । स्पष्ट रूप से मुझे यह जानने के लिए ठोस प्रकारों को जानने की जरूरत है कि कॉल करने के लिए कौन सा टकराव का पता लगाना है। यहां तक ​​कि डबल डिस्पैच भी नहीं है (जो किसी भी प्रकार की जाँच के लिए तार्किक रूप से अलग नहीं है) यहाँ मदद कर सकते हैं *।

इस स्थिति में अभ्यास में मैंने (बुलेट और हॉक) जो भौतिकी इंजन देखे हैं, वे एक या दूसरे प्रकार के परीक्षण पर निर्भर हैं।

मैं यह नहीं कह रहा हूं कि यह जरूरी एक अच्छा समाधान है, यह सिर्फ इतना है कि यह इस समस्या के संभावित समाधानों की एक छोटी संख्या का सबसे अच्छा हो सकता है

* तकनीकी तौर पर यह है एक खराब और जटिल तरीका है कि एन (N + 1) / 2 संयोजन (जहां N आकार प्रकार आप की संख्या है) की आवश्यकता होगी और केवल अंधेरा करना होगा क्या तुम सच में कर रहे हैं में डबल प्रेषण का उपयोग करना संभव है जो एक साथ दो आकृतियों के प्रकारों का पता लगा रहा है, इसलिए मैं इसे एक यथार्थवादी समाधान नहीं मानता।


1

कभी-कभी आप सभी वर्गों के लिए एक सामान्य विधि नहीं जोड़ना चाहते हैं क्योंकि यह वास्तव में उस विशिष्ट कार्य को करने के लिए उनकी जिम्मेदारी नहीं है।

उदाहरण के लिए आप कुछ संस्थाओं को आकर्षित करना चाहते हैं लेकिन ड्राइंग कोड को सीधे उनके साथ जोड़ना नहीं चाहते हैं (जो समझ में आता है)। एकाधिक प्रेषण का समर्थन नहीं करने वाली भाषाओं में आप निम्नलिखित कोड के साथ समाप्त हो सकते हैं:

void DrawEntity(Entity entity) {
    if (entity instanceof Circle) {
        DrawCircle((Circle) entity));
    else if (entity instanceof Rectangle) {
        DrawRectangle((Rectangle) entity));
    } ...
}

यह समस्या तब बन जाती है जब यह कोड कई स्थानों पर दिखाई देता है और नए एंटिटी प्रकार को जोड़ते समय आपको इसे हर जगह संशोधित करने की आवश्यकता होती है। यदि ऐसा है तो विजिटर पैटर्न का उपयोग करके इसे टाला जा सकता है लेकिन कभी-कभी केवल चीजों को सरल रखना बेहतर होता है और इसे ओवरगेंनेर नहीं करना चाहिए। वे परिस्थितियाँ हैं जब टाइप परीक्षण ठीक है।


0

केवल समय मैं उपयोग प्रतिबिंब के साथ संयोजन में है। लेकिन फिर भी गतिशील जाँच ज्यादातर, न कि किसी विशिष्ट वर्ग के लिए हार्ड-कोडेड (या केवल विशेष वर्गों के लिए इस तरह के रूप हार्ड-कोडेड है Stringया List)।

गतिशील जाँच से मेरा मतलब है:

boolean checkType(Type type, Object object) {
    if (object.isOfType(type)) {

    }
}

और हार्ड कोडित नहीं है

boolean checkIsManaer(Object object) {
    if (object instanceof Manager) {

    }
}

0

टाइप टेस्टिंग और टाइप कास्टिंग दो बहुत ही संबंधित अवधारणाएँ हैं। इतनी बारीकी से संबंधित कि मुझे यह कहने में आत्मविश्वास महसूस होता है कि आपको कभी भी एक प्रकार का परीक्षण नहीं करना चाहिए जब तक कि आपका इरादा परिणाम के आधार पर ऑब्जेक्ट को टाइप करने का न हो

जब आप आदर्श ऑब्जेक्ट-ओरिएंटेड डिज़ाइन के बारे में सोचते हैं, तो टाइप टेस्टिंग (और कास्टिंग) कभी नहीं होनी चाहिए । लेकिन उम्मीद है कि अब तक आपको पता चल गया है कि ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग आदर्श नहीं है। कभी-कभी, विशेष रूप से निचले स्तर के कोड के साथ, कोड आदर्श के लिए सही नहीं रह सकता है। जावा में ArrayLists के साथ ऐसा ही है; चूँकि वे रन-टाइम में यह नहीं जानते हैं कि एरे में किस क्लास को स्टोर किया जा रहा है, इसलिए वे Object[]एरेज़ बनाते हैं और स्टैटिकली उन्हें सही टाइप में कास्ट करते हैं।

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

टाइप टेस्टिंग भी अक्सर प्रतिबिंब में आती है। अक्सर आपके पास ऐसे तरीके होंगे जो वापस आते हैं Object[]या कुछ अन्य जेनेरिक सरणी होते हैं, और आप Fooकिसी भी कारण से सभी वस्तुओं को बाहर निकालना चाहते हैं । यह प्रकार परीक्षण और कास्टिंग का पूरी तरह से वैध उपयोग है।

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


1
ArrayListsपता नहीं क्‍लास रनटाइम पर स्‍टोर किया जा रहा है क्‍योंकि जावा में जेनेरिक नहीं था और जब उन्‍हें ओरेकल को पेश किया गया तो जेनरिक-कम कोड के साथ बैकवर्ड-कम्पेटिबिलिटी के लिए चुना गया। equalsएक ही मुद्दा है, और यह एक संदिग्ध डिजाइन निर्णय वैसे भी है; समानता की तुलना हर प्रकार के लिए मायने नहीं रखती है।
डोभाल

1
तकनीकी रूप से, जावा संग्रह किसी भी चीज़ में अपनी सामग्री नहीं डालते हैं। कंपाइलर प्रत्येक एक्सेस के स्थान पर टाइपकास्ट इंजेक्ट करता है: जैसेString x = (String) myListOfStrings.get(0)

पिछली बार मैंने जावा स्रोत को देखा था (जो कि 1.6 या 1.5 हो सकता है) अर्रेइलिस्ट स्रोत में एक स्पष्ट कलाकार था। यह अच्छे कारण के लिए एक (दबा हुआ) संकलक चेतावनी उत्पन्न करता है लेकिन वैसे भी अनुमति है। मुझे लगता है कि आप कह सकते हैं कि जेनेरिक को कैसे लागू किया जाता है, यह हमेशा Objectतब तक था जब तक कि इसे वैसे भी एक्सेस नहीं किया गया था; जावा में जेनरिक केवल संकलक कास्टिंग प्रदान करते हैं जो संकलक नियमों द्वारा सुरक्षित हैं।
मस्तूल

0

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

abstract class Vehicle
{
    abstract void Process();
}

class Car : Vehicle { ... }
class Boat : Vehicle { ... }
class Truck : Vehicle { ... }

अब कहते हैं कि हमारे व्यापार तर्क का शाब्दिक अर्थ है "सभी कारों को नावों और ट्रकों पर पूर्वता मिलती है"। Priorityवर्ग के लिए एक संपत्ति जोड़ना आपको इस व्यवसाय तर्क को सफाई से व्यक्त करने की अनुमति नहीं देता है क्योंकि आप इसके साथ समाप्त होंगे:

abstract class Vehicle
{
    abstract void Process();
    abstract int Priority { get }
}

class Car : Vehicle { public Priority { get { return 1; } } ... }
class Boat : Vehicle { public Priority { get { return 2; } } ... }
class Truck : Vehicle { public Priority { get { return 2; } } ... }

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

आपको निश्चित रूप से प्राथमिकताओं को स्थिरांक बनाना चाहिए और उन्हें खुद से एक कक्षा में रखना चाहिए, जो व्यापार तर्क को एक साथ रखने में मदद करता है:

static class Priorities
{
    public const int CAR_PRIORITY = 1;
    public const int BOAT_PRIORITY = 2;
    public const int TRUCK_PRIORITY = 2;
}

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

class VehicleScheduler : IScheduleVehicles
{
    public Vehicle WhichVehicleGoesFirst(Vehicle vehicle1, Vehicle vehicle2)
    {
        if(vehicle1 is Car) return vehicle1;
        if(vehicle2 is Car) return vehicle2;
        return vehicle1;
    }
}

यह व्यापारिक तर्क को लागू करने का सबसे सीधा तरीका है और अभी भी भविष्य में होने वाले परिवर्तनों के लिए सबसे अधिक लचीला है।


मैं आपके विशेष उदाहरण से सहमत नहीं हूं, लेकिन एक निजी संदर्भ होने के सिद्धांत से सहमत होगा जो विभिन्न अर्थों के साथ अलग-अलग प्रकार के हो सकते हैं। मैं एक बेहतर उदाहरण के रूप में जो सुझाव देता हूं वह एक ऐसा क्षेत्र होगा जो या तो पकड़ सकता nullहै String, या ए String[]। यदि 99% वस्तुओं को बिल्कुल एक स्ट्रिंग की आवश्यकता होगी, तो प्रत्येक स्ट्रिंग को एक अलग-अलग निर्माण में String[]संलग्न करना काफी भंडारण ओवरहेड जोड़ सकता है। Stringविल के सीधे संदर्भ का उपयोग करके एकल-स्ट्रिंग मामले को संभालने के लिए अधिक कोड की आवश्यकता होगी, लेकिन भंडारण को बचाएगा और चीजों को तेज कर सकता है।
सुपरकैट

0

टाइप टेस्टिंग एक उपकरण है, इसे बुद्धिमानी से उपयोग करें और यह एक शक्तिशाली सहयोगी हो सकता है। इसे खराब तरीके से इस्तेमाल करें और आपके कोड से बदबू आने लगेगी।

हमारे सॉफ़्टवेयर में हमें अनुरोधों के जवाब में नेटवर्क पर संदेश प्राप्त हुए। सभी deserialized संदेशों ने एक साझा आधार वर्ग साझा किया Message

कक्षाएं स्वयं बहुत सरल थीं, बस टाइप किए गए सी # गुणों के रूप में पेलोड और उन्हें मार्शलिंग और उन्हें अनमर्श करने के लिए रूटीन (वास्तव में मैंने संदेश प्रारूप के XML विवरण से t4 टेम्पलेट का उपयोग करके अधिकांश कक्षाएं उत्पन्न की थीं)

कोड कुछ इस तरह होगा:

Message response = await PerformSomeRequest(requestParameter);

// Server (out of our control) would send one message as response, but 
// the actual message type is not known ahead of time (it depended on 
// the exact request and the state of the server etc.)
if (response is ErrorMessage)
{ 
    // Extract error message and pass along (for example using exceptions)
}
else if (response is StuffHappenedMessage)
{
    // Extract results
}
else if (response is AnotherThingHappenedMessage)
{
    // Extract another type of result
}
// Half a dozen other possible checks for messages

दी गई, कोई यह तर्क दे सकता है कि संदेश वास्तुकला को बेहतर तरीके से डिजाइन किया जा सकता है लेकिन इसे बहुत पहले डिज़ाइन किया गया था और सी # के लिए नहीं तो यह वही है जो यह है। यहाँ प्रकार परीक्षण ने हमारे लिए एक बहुत ही जर्जर तरीके से एक वास्तविक समस्या को हल किया।

ध्यान देने योग्य बात यह है कि C # 7.0 का पैटर्न मिलान हो रहा है (जो कि कई मायनों में स्टेरॉयड पर परीक्षण का प्रकार है) यह सब नहीं हो सकता है ...


0

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


ठीक है, कुछ JSON ड्यूरिसेलाइज़र एक ऑब्जेक्ट ट्री को तुरंत हटाने का प्रयास करेंगे यदि आपकी संरचना में या तो "इंफ्रक्शन फ़ील्ड" के लिए टाइप जानकारी है। लेकिन हाँ। मुझे लगता है कि यह वह दिशा है, जिसमें कार्ल बी का जवाब था।
23
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.