2009-04-14 9 views
70

मैंने एनम-जैसे मानों को संभालने और बनाए रखने के सर्वोत्तम तरीके के बारे में कई प्रश्न/चर्चाएं देखी हैं (उदाहरण के लिए Persisting data suited for enums, How to persist an enum using NHibernate), और मैं पूछना चाहता हूं कि सामान्य consenus क्या है ।enums का उपयोग करने और बनाए रखने के लिए सर्वोत्तम अभ्यास

विशेष रूप से:

  • कैसे ये मान कोड में नियंत्रित किया जाना चाहिए?
  • उन्हें किसी डेटाबेस (पाठ के रूप में/संख्या के रूप में) पर कैसे रखा जाना चाहिए?
  • विभिन्न समाधानों के ट्रेडऑफ क्या हैं?

नोट: मैंने मूल रूप से इस प्रश्न में उत्तर में दिए गए स्पष्टीकरण को उत्तर में ले जाया।

उत्तर

3

मैंने अपनी समझ को सारांशित करने का प्रयास किया है। यदि आपके पास कोई सुधार है तो इसे संपादित करने के लिए स्वतंत्र महसूस करें। तो यहाँ यह जाता है:

कोड

कोड में, enums या तो भाषा की मूल enum प्रकार (कम से कम में जावा और सी #) का उपयोग कर नियंत्रित किया जाना चाहिए, या "typesafe enum pattern" की तरह कुछ का उपयोग कर। सादा स्थिरांक (इंटीजर या इसी तरह) का उपयोग करना निराश होता है, क्योंकि आप प्रकार की सुरक्षा खो देते हैं (और यह समझना मुश्किल हो जाता है कि कौन से मान कानूनी विधि हैं जैसे कि विधि।)।

इन दोनों के बीच चुनाव कितना अतिरिक्त कार्यक्षमता enum के साथ संलग्न किया जा सकता है पर निर्भर करता है:

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

विशेष रूप से, कम से कम जावा में एक enum अन्य वर्ग से विरासत में नहीं कर सकते हैं, इसलिए यदि आप समान व्यवहार के साथ कई enums जो आपको एक सुपर क्लास में डाल करना चाहते हैं, तो आप जावा के enums उपयोग नहीं कर सकते।

बने enums

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

  • सॉफ्टवेयर में, हर enum तो मानचित्रण कार्य enum (सॉफ्टवेयर के अंदर उपयोग के लिए) और (निरंतर आ रही है के लिए) आईडी मूल्य के बीच परिवर्तित करने के लिए होना चाहिए। कुछ ढांचे (उदा। (एन) हाइबरनेट) ने स्वचालित रूप से ऐसा करने के लिए सीमित समर्थन दिया है। अन्यथा, आपको इसे enum प्रकार/वर्ग में रखना होगा।
  • डेटाबेस (आदर्श) कानूनी मानों को सूचीबद्ध करने वाले प्रत्येक enum के लिए एक तालिका होना चाहिए। एक कॉलम आईडी होगा (ऊपर देखें), जो पीके है। अतिरिक्त कॉलम उदाहरण के लिए समझ में आ सकते हैं विवरण। सभी तालिका कॉलम जिनमें उस enum से मान शामिल होंगे, फिर इस "enum table" को FK के रूप में उपयोग कर सकते हैं। यह गारंटी देता है कि गलत enum मानों को कभी भी जारी नहीं किया जा सकता है, और डीबी को "अपने आप पर खड़े होने" की अनुमति देता है।

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

  • केवल डीबी में मानों की सूची रखें, निर्माण समय पर enum प्रकार उत्पन्न करें। सुरुचिपूर्ण, लेकिन इसका मतलब है कि एक निर्माण के लिए एक डीबी कनेक्शन की आवश्यकता है, जो समस्याग्रस्त लगता है।
  • आधिकारिक होने के लिए कोड में मानों की सूची को परिभाषित करें। रनटाइम पर आमतौर पर डीबी में मानों के खिलाफ जांचें (आमतौर पर स्टार्टअप पर), मेलसमूह पर शिकायत/निरस्त करें।
2

IMHO, कोड भाग के लिए के रूप में:

आप हमेशा अपने enumerations के लिए 'enum' प्रकार का उपयोग करना चाहिए, मूल रूप से आप मुफ्त की बहुत पाने के लिए यदि आप कार्य करें: प्रकार की सुरक्षा, कैप्सूलीकरण और स्विच परिहार, EnumSet और EnumMap और कोड स्पष्टता जैसे कुछ संग्रहों का समर्थन।

दृढ़ता भाग के लिए आप हमेशा enum के स्ट्रिंग प्रतिनिधित्व को जारी रख सकते हैं और enum.valueOf (स्ट्रिंग) विधि का उपयोग करके इसे वापस लोड कर सकते हैं।

+0

सैद्धांतिक रूप से सहमत हैं, हालांकि कम से कम जावा में "enum" सीमित है जिसमें यह एक सुपरक्लास (ऊपर उल्लिखित) नहीं हो सकता है, इसलिए कभी-कभी "टाइपएफ़ एनम" कक्षा शायद बेहतर होती है। – sleske

3

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

+1

enum का int मान समय के साथ समान होने की गारंटी नहीं है। –

+0

इसके अलावा, यदि आप एक छोटी स्ट्रिंग का उपयोग करते हैं, तो प्रदर्शन समान होना चाहिए। एक चार (2) 2 बाइट लेता है, एक इंट आमतौर पर 2 या 4 लेता है। – sleske

+6

@ मिगुएल पिंग: विचार * स्पष्ट रूप से * प्रत्येक enum को एक आईडी (int या char) assing। Enum के आंतरिक रूप से उत्पन्न int का उपयोग वास्तव में बहुत खतरनाक है। – sleske

5

जावा या सी # हमेशा कोड में enums का उपयोग करना चाहिए। अस्वीकरण: मेरी पृष्ठभूमि सी # है।

यदि मान डेटाबेस के लिए जारी रखा जाना है, तो प्रत्येक गणना सदस्य के अभिन्न मूल्यों को स्पष्ट रूप से परिभाषित किया जाना चाहिए ताकि कोड में बाद में परिवर्तन गलती से अनुवादित enum मानों और इस प्रकार अनुप्रयोग व्यवहार को बदल नहीं सके।

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

यदि आप सीआरयूडी कोड उत्पन्न करने के लिए एक उपकरण का उपयोग करते हैं तो यह कॉलम के लिए उपयोग करने के लिए एक गणना प्रकार को परिभाषित करने में सक्षम होना चाहिए ताकि उत्पन्न कोड ऑब्जेक्ट्स हमेशा गणित सदस्यों का उपयोग कर सकें।

  • आप एक enum MyEnum है, तो एक स्थिर वर्ग MyEnumInfo जो enum बारे में अतिरिक्त जानकारी की खोज करने के लिए उपयोगिता तरीके प्रदान करता है बनाने के लिए:

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

  • अतिरिक्त पैरामीटर निर्दिष्ट करने के लिए गुणों के साथ गणना सदस्यों को सजाने के लिए। उदाहरण के लिए हमने एक EnumDropDown नियंत्रण विकसित किया है जो एक एएसपी बनाता है।एनईटी ड्रॉपडाउन गणना मूल्यों से भरा है, और एक EnumDisplayAttribute प्रत्येक सदस्य के लिए उपयोग करने के लिए अच्छी तरह से स्वरूपित प्रदर्शन टेक्स्ट निर्दिष्ट करता है।

मैं इस प्रयास नहीं किया है, लेकिन SQL सर्वर 2005 या बाद में, आप सैद्धांतिक रूप से डेटाबेस है कि enum जानकारी और विचारों या अन्य निर्माणों में उपयोग के लिए enums करने के लिए मूल्यों को परिवर्तित करने की क्षमता शामिल हैं के साथ सी # कोड रजिस्टर कर सकता है, डीबीए के उपयोग के लिए डेटा को आसान तरीके से अनुवाद करने का एक तरीका बनाना।

+0

+1 स्पष्ट रूप से मान निर्दिष्ट करना "भ्रष्टाचार" से बचने का एकमात्र तरीका है जब आप enum – ashes999

5

सी # के लिए कोड हैंडलिंग में आप 0 मान को डेलकाइंग परिभाषित करने से चूक गए हैं। मैं लगभग बिना हमेशा की तरह मेरी पहली मूल्य की घोषणा विफल:

public enum SomeEnum 
{ 
    None = 0, 
} 

तो के रूप में एक शून्य मान के रूप में सेवा करने के लिए। चूंकि बैकिंग प्रकार एक पूर्णांक है और एक पूर्णांक 0 पर डिफ़ॉल्ट है, इसलिए यह जानने के लिए कई जगहों पर बड़े पैमाने पर उपयोगी है कि वास्तव में एक enum प्रोग्रामेटिक रूप से सेट किया गया है या नहीं।

+5

को असहमत करते हैं। यह केवल तभी समझ में आता है जब आप कभी-कभी वैरिएबल को अनियंत्रित छोड़ देते हैं, जिसे मैं गंभीर रूप से खराब अभ्यास पर विचार करता हूं। मैंने अक्सर "कोई नहीं" मान रखने के इस विचार को देखा है, लेकिन मेरा मानना ​​है कि यह केवल वास्तविक समस्या (अनियमित चर) को छुपाता है। – sleske

+2

यह समस्या को कैसे छिपाता है? यह एक निरर्थक int की तरह स्पष्ट बनाता है। मैं कोड में अनियमित मूल्यों को छोड़ देता हूं क्योंकि मुझे पता है कि सीएलआर उन्हें डिफ़ॉल्ट रूप से सेट करेगा। वे अभी भी इसके सिर्फ अंतर्निहित शुरू कर रहे हैं। – Quibblesome

+0

अच्छा, यह शायद शैली का मामला है। मैं दृढ़ता से सभी चरों को पूरी तरह शुरू करने में दृढ़ विश्वास करता हूं (या अधिकतर अगर घोषणा के बाद सीधे अगर)। अन्यथा आप उन्हें प्रारंभ करना भूल सकते हैं, खासकर यदि कोड प्रवाह जटिल है। Http://c2.com/cgi/wiki?SingleStepConstructor भी देखें। – sleske

3

ठीक है, मेरे अनुभव से, किसी तत्काल विधि कॉल में गुजरने के विकल्प (झंडे के रूप में) के अलावा किसी अन्य चीज़ के लिए enums का उपयोग करके, परिणामस्वरूप switch-कुछ बिंदु पर परिणाम।

  • आप तो आप कोड कि इतने बनाए रखने के लिए (कुख्यात switch बयान)
  • विस्तार enums आसान नहीं है के साथ खत्म हो सकता है, सब अपने कोड पर enum का उपयोग करने जा रहे हैं, तो एक दर्द है। आप एक नई enum आइटम जोड़ते हैं और सभी शर्तों की जांच के लिए अपने सभी कोड के माध्यम से जाने में समाप्त होते हैं।
  • .NET 3.5 के साथ, आप कक्षाओं की तरह थोड़ा और व्यवहार करने के लिए enum के लिए एक्सटेंशन विधियां जोड़ सकते हैं। हालांकि, वास्तविक कार्यक्षमता इस तरह से इतना आसान है, क्योंकि यह अभी भी एक वर्ग नहीं है नहीं है जोड़ने (आप अपने विस्तार के तरीकों में switch -es का उपयोग कर अगर कहीं और नहीं खत्म होगा।

तो थोड़ा के साथ एक enum की तरह संस्था के लिए कार्यक्षमता की अधिक आपके मन में कई चीजों के साथ, कुछ समय लगेगा और एक वर्ग के रूप में बनाना चाहिए:

  • अपनी कक्षा एक enum की तरह व्यवहार करने के लिए, आप या तो एक सिंगलटन के रूप में दृष्टांत के लिए प्रत्येक व्युत्पन्न वर्ग मजबूर कर सकते हैं, या अलग-अलग उदाहरणों की मूल्य तुलना की अनुमति देने के लिए बराबर ओवरराइड करें।
  • यदि आपकी कक्षा enum-like है, तो इसका मतलब होना चाहिए था टी में कोई धारावाहिक स्थिति नहीं होनी चाहिए - deserialization अकेले अपने प्रकार से संभव होना चाहिए (जैसा कि आपने कहा था "एक प्रकार का" आईडी)।
  • दृढ़ता तर्क केवल बेस क्लास तक सीमित होना चाहिए, अन्यथा आपका "enum" विस्तार करना एक दुःस्वप्न होगा। यदि आप सिंगलटन पैटर्न के लिए गए हैं, तो आपको सिंगलटन उदाहरणों में उचित deserialization सुनिश्चित करने की आवश्यकता होगी।
3

प्रत्येक बार जब आप कोड में "जादू संख्या" का उपयोग करके स्वयं को पाते हैं तो enums में बदल जाते हैं। समय बचत के अलावा (जब बग आते हैं तो जादू गायब हो जाएगा ...) यह आपकी आंखों और स्मृति को बचाएगा (सार्थक enums कोड को और अधिक पठनीय और स्वयं-दस्तावेज बनाना), अनुमान लगाने के बाद - आप संभवतः बनाए रखने और विकसित करने वाले व्यक्ति हैं आपका स्वयं का कोड

12

प्रारंभिक लेख मुझे ठीक दिखता है। फिर भी, टिप्पणियों के आधार पर, ऐसा लगता है कि जावा एनम्स से संबंधित कुछ टिप्पणियां कुछ चीजों को स्पष्ट कर सकती हैं।

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

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

दस्तावेज़ीकरण के बिंदु पर भी विचार करें - क्या आप डेटाबेस में अपने enum के वास्तविक अर्थ को दस्तावेज करना चाहते हैं? स्रोत कोड (आपके enum प्रकार) या कुछ बाहरी दस्तावेज में प्रतिबिंबित स्रोत कोड में? मैं व्यक्तिगत रूप से स्रोत कोड पसंद करता हूं।

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

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

+1

समर्थन करने के लिए दो परिदृश्य हैं: कोई मेरी फ़ाइल में enums reordering या कोई कुछ refactoring (गरीब प्रारंभिक नाम विकल्पों को स्पष्ट करने के लिए) और लगातार डेटा तोड़ने। मुझे लगता है कि उत्तरार्द्ध अधिक महत्वपूर्ण है, और डेटा निरंतरता के लिए जाने का तरीका है। – Justin

18

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

1

मुझे पता है कि यह एक पुराना मंच है, क्या होगा यदि डेटाबेस में अन्य चीजें सीधे एकीकृत हो सकती हैं? जैसे जब परिणामी डीबी कोड का एकमात्र उद्देश्य है। फिर, आप प्रत्येक एकीकरण पर enums परिभाषित करेंगे। बेहतर तो उन्हें डीबी में रखने के लिए। अन्यथा, मैं मूल पोस्ट से सहमत हूं।