क्या एकल कॉन्फिग ऑब्जेक्ट एक बुरा विचार है?


43

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

एक समस्या यह है कि आप आमतौर पर उसी कॉन्फ़िगरेशन के साथ परीक्षण नहीं करना चाहते हैं जिसे आप चलाते हैं। इस के लिए कुछ उपाय हैं:

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

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

मैं इस निष्कर्ष पर आना शुरू कर रहा हूं कि इस तरह की कॉन्फिग ऑब्जेक्ट एक बुरा विचार है। तुम क्या सोचते हो? कुछ विकल्प क्या हैं? और आप हर जगह कॉन्फ़िगरेशन का उपयोग करने वाले एप्लिकेशन को कैसे फिर से शुरू करना चाहते हैं?


डिस्क डिस्क पर एक फ़ाइल को पढ़कर अपनी सेटिंग्स प्राप्त करता है, है ना? तो क्यों न केवल एक "test.config" फ़ाइल है जिसे वह पढ़ सकता है?
आनन।

यह पहली समस्या को हल करता है, लेकिन दूसरा नहीं।
JW01

@ JW01: क्या आपके सभी परीक्षणों को स्पष्ट रूप से अलग विन्यास की आवश्यकता है? आप उस विन्यास को अपने परीक्षण के दौरान कहीं स्थापित करने जा रहे हैं, क्या आप नहीं हैं?
आनन।

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

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

जवाबों:


35

मेरे पास एकल कॉन्फिग ऑब्जेक्ट के साथ कोई समस्या नहीं है, और सभी सेटिंग्स को एक स्थान पर रखने में फायदा देख सकते हैं।

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

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

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


7

मैं इंटरफेस के साथ संबंधित सेटिंग्स के समूहों को अलग करता हूं।

कुछ इस तरह:

public interface INotificationEmailSettings {
   public string To { get; set; }
}

public interface IMediaFileSettings {
    public string BasePath { get; set; }
}

आदि।

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

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

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

इसलिए, मैं एक स्थिर वर्ग को एक मुखौटा के रूप में उपयोग करूंगा, कहीं से भी सेटिंग में आसान प्रविष्टि प्रदान कर सकता हूं, और उस स्थिर वर्ग के भीतर मैं इंटरफ़ेस के कार्यान्वयन का पता लगाऊंगा और सेटिंग प्राप्त करूंगा।

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

public static class AllSettings {
    public INotificationEmailSettings NotificationEmailSettings {
        get {
            return ServiceLocator.Get<INotificationEmailSettings>();
        }
    }
}

मुझे यह मिश्रण पूरी दुनिया में सबसे अच्छा लगता है।


3

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

(अप्रचलित संदर्भ: लिगेसी कोड के साथ प्रभावी रूप से कार्य करना । इसमें इकाई परीक्षण विरासत कोड के लिए यह और कई अमूल्य तरकीबें शामिल हैं।)

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

यह पहले से ही एक वास्तविक निर्भरता इंजेक्शन समाधान के करीब हो रहा है, हालांकि, जावा के लिए स्प्रिंग की तरह। यदि आप इस तरह के ढांचे में पलायन कर सकते हैं, तो ठीक है। लेकिन वास्तविक जीवन में, और विशेष रूप से जब एक विरासत ऐप से निपटते हैं, तो अक्सर डीआई के प्रति धीमी और सावधानीपूर्वक प्रतिक्रिया करना सबसे अच्छा समझौता होता है।


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

1
@ क्रोन, ये विरासत कोड यूनिट को परीक्षण योग्य बनाने के लिए बेहद उपयोगी और व्यावहारिक तरकीबें हैं, और मैं उनका बड़े पैमाने पर उपयोग कर रहा हूं। हालांकि, आदर्श रूप से उत्पादन कोड में परीक्षण के उद्देश्य के लिए पूरी तरह से शुरू की गई कोई भी विशेषताएं शामिल नहीं होनी चाहिए। लंबे समय में, यह वह जगह है जहां मैं फिर से कदम बढ़ा रहा हूं।
Péter Török

2

मेरे अनुभव में, जावा दुनिया में, यह वही है जो वसंत के लिए है। यह कुछ गुणों के आधार पर ऑब्जेक्ट्स (सेम) बनाता है और प्रबंधित करता है जो रनटाइम पर सेट होते हैं, और अन्यथा आपके एप्लिकेशन के लिए पारदर्शी होते हैं। आप परीक्षण के साथ जा सकते हैं। फाइल जो Anon है। उल्लेख या आप कॉन्फ़िगर फ़ाइल में कुछ तर्क शामिल कर सकते हैं जो स्प्रिंग कुछ अन्य कुंजी (जैसे होस्टनाम या पर्यावरण) के आधार पर गुणों को सेट करने के लिए संभाल लेगा।

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


2

यह प्रश्न वास्तव में कॉन्फ़िगरेशन की तुलना में अधिक सामान्य समस्या के बारे में है। जैसे ही मैंने "सिंगलटन" शब्द पढ़ा, मैंने तुरंत उस पैटर्न से जुड़ी सभी समस्याओं के बारे में सोचा, जिनमें से कम से कम खराब परीक्षणशीलता नहीं है।

सिंगलटन पैटर्न "हानिकारक माना जाता है"। मतलब, यह हमेशा गलत बात नहीं है, लेकिन यह आमतौर पर है। यदि आप कभी भी किसी चीज़ के लिए सिंगलटन पैटर्न का उपयोग करने पर विचार कर रहे हैं , तो विचार करना बंद करें:

  • क्या आपको कभी इसे उप-करने की आवश्यकता होगी?
  • क्या आपको कभी किसी इंटरफ़ेस पर प्रोग्राम करने की आवश्यकता होगी?
  • क्या आपको इसके खिलाफ इकाई परीक्षण चलाने की आवश्यकता है?
  • क्या आपको इसे बार-बार संशोधित करने की आवश्यकता होगी?
  • क्या आपका आवेदन कई उत्पादन प्लेटफार्मों / वातावरणों का समर्थन करने का इरादा है?
  • क्या आप स्मृति उपयोग के बारे में भी थोड़ा चिंतित हैं?

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

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


0

एक तरह से मैंने इसे C # में संभाला है एक सिंगलटन ऑब्जेक्ट है जो इंस्टेंस क्रिएशन के दौरान लॉक करता है और सभी क्लाइंट ऑब्जेक्ट्स द्वारा उपयोग के लिए एक बार में सभी डेटा को इनिशियलाइज़ करता है। यदि यह सिंगलटन कुंजी / मान जोड़े को संभाल सकता है, तो आप कई अलग-अलग क्लाइंट और क्लाइंट प्रकारों द्वारा उपयोग के लिए जटिल कुंजी सहित डेटा के किसी भी तरीके को स्टोर कर सकते हैं। यदि आप इसे गतिशील रखना चाहते हैं और आवश्यकतानुसार नए क्लाइंट डेटा को लोड करना चाहते हैं, तो आप क्लाइंट की मुख्य कुंजी के अस्तित्व को सत्यापित कर सकते हैं और यदि गायब है, तो उस क्लाइंट के डेटा को मुख्य शब्दकोश में जोड़कर लोड करें। यह भी संभव है कि प्राथमिक कॉन्फिग सिंग्लटन में क्लाइंट डेटा के सेट हो सकते हैं जहां एक ही क्लाइंट प्रकार के गुणक एक ही डेटा का उपयोग करते हैं जो सभी प्राथमिक कॉन्फिग सिंगलटन के माध्यम से एक्सेस करते हैं। इस config ऑब्जेक्ट संगठन में से अधिकांश इस बात पर निर्भर हो सकता है कि आपके config क्लाइंट को इस जानकारी तक पहुँचने की आवश्यकता है या नहीं और यह जानकारी स्थिर या गतिशील है। मैंने कुंजी / मान दोनों के साथ-साथ विशिष्ट ऑब्जेक्ट API का उपयोग किया है, जिसके आधार पर इसकी आवश्यकता थी।

मेरा एक कॉन्फिग सिंगल एक डेटाबेस से पुनः लोड करने के लिए संदेश प्राप्त कर सकता है। इस मामले में, मैं एक दूसरे ऑब्जेक्ट संग्रह में लोड करता हूं और केवल मुख्य संग्रह को स्वैप करने के लिए लॉक करता हूं। यह अन्य थ्रेड्स पर अवरोधन को रोकता है।

यदि कॉन्फ़िगरेशन फ़ाइलों से लोड हो रहा है, तो आपके पास फ़ाइल पदानुक्रम हो सकती है। एक फ़ाइल में सेटिंग्स निर्दिष्ट कर सकती हैं कि कौन सी अन्य फ़ाइल लोड करनी है। मैंने इस तंत्र का उपयोग C # विंडोज सेवाओं के साथ किया है जिसमें कई, वैकल्पिक घटक हैं, जिनमें से प्रत्येक अपनी स्वयं की कॉन्फ़िगरेशन सेटिंग्स के साथ है। फ़ाइल संरचना पैटर्न फ़ाइलों में समान थे, लेकिन प्राथमिक फ़ाइल सेटिंग्स के आधार पर लोड किए गए थे या नहीं।


0

आप जिस वर्ग का वर्णन कर रहे हैं, वह फ़ंक्शंस के बजाय डेटा के अलावा एक ईश्वर ऑब्जेक्ट एंटीपैटर की तरह लग रहा है । यह एक समस्या है। आपको किसी उचित कारण से डेटा को फिर से पढ़ते समय संभवतः उचित डेटा में कॉन्फ़िगर किया गया डेटा संग्रहीत और संग्रहीत होना चाहिए।

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

अंत में, इस तरह के एक वैश्विक राज्य का निर्माण एनकैप्सुलेशन का उल्लंघन है क्योंकि आप डेटा को सीधे जानने के लिए आवश्यकता से अधिक कक्षाओं की अनुमति दे रहे हैं।


0

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

मैं लॉस टेकीज के जोशुआ फलागन द्वारा इस मामले पर प्रेरित था; उन्होंने कुछ समय पहले इस मामले पर एक लेख लिखा था।

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