2012-03-16 10 views
10

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

तो, इस विशिष्ट प्रकार की स्थितियों के अलावा, मैं यह समझाना चाहता हूं कि समान रूप से लागू करने के लिए सर्वोत्तम प्रथाएं क्या हैं, अर्थात् तकनीकी रूप से नहीं।

+10

'यूनिट-टेस्ट लिखते समय, मुझे अक्सर स्थिति का सामना करना पड़ता है जब परीक्षण में कुछ ऑब्जेक्ट के बराबर() - निष्कर्षों में - वास्तविक वातावरण में यह कैसे काम करता है उससे अलग काम करना चाहिए।' आप इसे कभी क्यों चाहेंगे? – helpermethod

+1

क्योंकि पहचान, और समानता है। कई स्थितियों के लिए, पहचान काफी अच्छी है, खासकर डीबी ऐप्स में। परीक्षण में, हालांकि, आसानी से स्थितियां हो सकती हैं जहां आप हैं, शायद "प्रतिलिपि" प्रतिलिपि की तुलना में "पहले" प्रतिलिपि बनाने के लिए, और इन मामलों में पहचान पर्याप्त नहीं है, आप गहराई से जाना चाहते हैं वस्तु के वास्तविक क्षेत्रों। –

उत्तर

16

समान रूप से लागू करने के लिए सर्वोत्तम प्रथाएं क्या हैं, अर्थात् तकनीकी रूप से नहीं।

जावा में equals विधि वास्तव में, क्योंकि यह कैसे Collection और Map कार्यान्वयन के साथ एकीकृत के "identity equals" होने के लिए विचार किया जाना चाहिए। निम्नलिखित पर विचार करें:

public class Foo() { 
    int id; 
    String stuff; 
} 

Foo foo1 = new Foo(10, "stuff"); 
fooSet.add(foo1); 
... 
Foo foo2 = new Foo(10, "other stuff"); 
fooSet.add(foo2); 

तो Foo पहचान id क्षेत्र तो 2 fooSet.addनहींSet के लिए एक और तत्व जोड़ना चाहिए है लेकिन जब से वे एक ही id है पहले एक प्रतिस्थापित करना चाहिए। यदि आप (और हैशकोड) विधि को दोनोंid और stuff फ़ील्ड्स को शामिल करने के लिए परिभाषित करते हैं तो यह टूटा जाएगा और Set में उसी आईडी फ़ील्ड वाले ऑब्जेक्ट में 2 संदर्भ हो सकते हैं।

आप एक Collection (या Map) में वस्तुओं के भंडारण नहीं कर रहे हैं, तो आप equals विधि इस तरह से परिभाषित करने के लिए की जरूरत नहीं है, लेकिन यह कई द्वारा माना जाता है बुरा रूप माना जाता है। यदि भविष्य में आप इसे Collection पर संग्रहीत करें तो चीजें तोड़ दी जाएंगी।

यदि सभी क्षेत्रों की समानता के परीक्षण के लिए की आवश्यकता है, तो मैं एक और तरीका लिखता हूं। कुछ equalsAllFields(Object obj) या कुछ ऐसे जैसे।

तो फिर तुम जैसे कुछ करना होगा:

assertTrue(obj1.equalsAllFields(obj2)); 

इसके अलावा, एक उचित व्यवहार equals तरीकों जिसके खाते परिवर्तनशील क्षेत्रों में रखना परिभाषित नहींहै। जब हम कक्षा पदानुक्रमों के बारे में बात करना शुरू करते हैं तो समस्या भी मुश्किल हो जाती है।परिभाषित बराबर:

Point p = new Point(1, 2); 
// ColoredPoint extends Point 
ColoredPoint c = new ColoredPoint(1, 2, Color.RED); 
// this is true because both points are at the location 1, 2 
assertTrue(p.equals(c)); 
// however, this would return false because the Point p does not have a color 
assertFalse(c.equals(p)); 

कुछ और पढ़ने मैं अत्यधिक की सिफारिश करेंगे "ख़तरा # 3 है: एक बच्चे वस्तु अपने स्थानीय क्षेत्रों और आधार वर्ग equals तो इसकी समरूपता का उल्लंघन किया गया का एक संयोजन के रूप में परिभाषित करता है, तो equals

How to Write an Equality Method in Java

कुछ additio: इस महान पेज में परिवर्तनशील क्षेत्रों में "खंड संदर्भ में एनएएल लिंक:

ओह, और सिर्फ भावी पीढ़ी के लिए, आप क्या समानता निर्धारित करने के लिए की तुलना करने के लिए चुन-प्रांगण की परवाह किए बिना, आप hashCode गणना में एक ही क्षेत्रों का उपयोग करने की आवश्यकता है । equals और hashCode सममित होना चाहिए। यदि दो ऑब्जेक्ट बराबर हैं, तो में एक ही हैश-कोड होना चाहिए। विपरीत जरूरी नहीं है।

+0

"यदि आप संग्रह में अपनी ऑब्जेक्ट्स को संग्रहीत नहीं कर रहे हैं तो आपको पहचान के आधार पर बराबर विधि को परिभाषित करने की आवश्यकता नहीं है" ओओ का पूरा बिंदु यह है कि आपकी कक्षा एक स्वतंत्र अमूर्तता का प्रतिनिधित्व करती है। आप विशेष प्रयोजनों के लिए आवश्यकताओं को लीक कर रहे हैं और अपने वर्ग इंटरफ़ेस को ट्वीव कर देंगे जो इसे अर्थहीन प्रदान कर सकता है। – DPM

+0

हालांकि अकादमिक रूप से सही @ जुबत, जावा 'संग्रह का प्रभाव जेडीके के लगभग हर हिस्से के बारे में है। आप उन्हें ओओ शुद्धवादी होने के लिए अनदेखा कर सकते हैं लेकिन आप अपने जोखिम पर ऐसा करते हैं। – Gray

+0

अभी भी लगता है कि क्लास प्रलेखन में अद्वितीय पहचानकर्ताओं जैसे इनवेरिएंट का प्रतिनिधित्व करना बेहतर है और उनके लिए दावा का उपयोग करें (नीचे मेरा उत्तर देखें) – DPM

3

Object.equals(Object obj) जावाडोक से कॉपी किया गया:

बताता है कि क्या किसी अन्य वस्तु यह एक "के बराबर है।"

  • यह कर्मकर्त्ता है: के लिए किसी भी गैर-शून्य संदर्भ मूल्य एक्स, x.equals (एक्स) सच लौट जाना

    विधि के बराबर होती है गैर-शून्य वस्तु संदर्भों पर एक तुल्यता संबंध लागू करता है।

  • यह सममित है: किसी भी गैर-शून्य संदर्भ मानों x और y, x.equals (y) के लिए सत्य वापस आना चाहिए यदि केवल y.equals (x) सत्य लौटाता है।
  • यह संक्रमणीय है: किसी भी गैर-शून्य संदर्भ मान x, y, और z के लिए, यदि x.equals (y) सत्य लौटाता है और y.equals (z) सत्य लौटाता है, तो x.equals (z) को सत्य वापस करना चाहिए ।
  • यह संगत है: किसी भी गैर-शून्य संदर्भ मान x और y के लिए, x.equals (y) के एकाधिक आमंत्रण लगातार सत्य या लगातार लौटते हैं, बशर्ते ऑब्जेक्ट्स पर तुलना के बराबर उपयोग की जाने वाली कोई भी जानकारी संशोधित न हो।
  • किसी भी गैर-शून्य संदर्भ मान x के लिए, x.equals (शून्य) झूठी वापसी करनी चाहिए।

यह मेरे लिए बहुत स्पष्ट है, इस तरह बराबर काम करना चाहिए। किस क्षेत्र के लिए चुनने के लिए, आप चुनते हैं कि निर्धारित करने के लिए फ़ील्ड का जो भी संयोजन आवश्यक है, चाहे कोई अन्य वस्तु इस "बराबर" हो।

आपके विशिष्ट मामले के लिए, यदि आप अपने परीक्षण में समानता के लिए व्यापक दायरे की आवश्यकता रखते हैं, तो आप इसे अपने परीक्षण में लागू करते हैं। आपको इसे फिट करने के लिए अपने बराबर विधि को हैक नहीं करना चाहिए।

+0

सहमत हुए। इसके अलावा मैंने अपने उत्तर में _common sense_ का "छद्म" नियम जोड़ा। –

1

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

यह प्रभावी जावा से है:

कक्षा में प्रत्येक "महत्वपूर्ण" क्षेत्र के लिए, यदि तर्क के उस क्षेत्र इस वस्तु की इसी क्षेत्र से मेल खाता है की जाँच करें।

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

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

+2

"यदि आप आईडी से मेल खाना चाहते हैं क्योंकि यह उस वर्ग के लिए एक अद्वितीय पहचानकर्ता है तो आईडी आईडी की तुलना करें, उस मामले में बराबर का उपयोग न करें।" - 'सेट' या 'मानचित्र' में उदाहरण डालने के बारे में क्या? मुझे इसके लिए सही बराबर की आवश्यकता है, और यह आईडी द्वारा उनकी तुलना कर रहा है। इस स्थिति के बारे में क्या? –

+0

@AndreyBalaguta: यदि आईडी वास्तव में अद्वितीय होना चाहिए, तो 'बराबर' को ओवरराइड करना आवश्यक नहीं होना चाहिए। इसके बजाय, किसी भी दिए गए आईडी के लिए मौजूद 'केवल टाइप' के संदर्भ में रखने के लिए 'मैप <इंटेगर, योर टाइप' 'का उपयोग करें। – supercat

0

मुझे लगता है कि equals() विधि को ओवरराइड करते समय केवल एकमात्र सर्वोत्तम अभ्यास सामान्य ज्ञान है।

जावा एपीआई के equivalence definition के अलावा कोई नियम नहीं हैं। एक बार जब आप समानता की परिभाषा को चुन लेते हैं, तो आपको इसे अपने hashCode() विधि पर भी लागू करना होगा।

इसका मतलब है कि, एक डेवलपर के रूप में, आप, आपके सहकर्मियों और रखरखावकर्ताओं को पता होना चाहिए कि आपके उदाहरण का कहना है कि Watermelon एक अन्य ऑब्जेक्ट उदाहरण के बराबर है।

1

इकाई परीक्षण के बारे में मुझे equals() लिखते समय दोनों अलग नहीं होंगे।

आप equals() और hashcode() को लागू करने से संपत्ति में से एक या समूह के साथ प्रत्येक वस्तु की समानता परिभाषित करते हैं।

यदि आप ऑब्जेक्ट के सभी गुणों की तुलना करना चाहते हैं तो अपने परीक्षण में, तो जाहिर है कि आपको प्रत्येक विधि को कॉल करने की आवश्यकता है।

मुझे लगता है कि उन्हें अलग से इलाज करना बेहतर है।

संबंधित मुद्दे