एप्लिकेशन सेटिंग लोड करने का सबसे अच्छा तरीका है


24

जावा एप्लिकेशन की सेटिंग्स को रखने का एक सरल तरीका एक विशिष्ट मान के साथ जुड़े प्रत्येक सेटिंग के पहचानकर्ता वाले ".properties" एक्सटेंशन के साथ एक पाठ फ़ाइल द्वारा दर्शाया गया है (यह मान एक संख्या, स्ट्रिंग, दिनांक, आदि हो सकता है।) । C # एक समान दृष्टिकोण का उपयोग करता है, लेकिन पाठ फ़ाइल का नाम "App.config" होना चाहिए। दोनों ही मामलों में, स्रोत कोड में आपको सेटिंग पढ़ने के लिए एक विशिष्ट वर्ग को इनिशियलाइज़ करना होगा: इस क्लास में एक विधि है जो निर्दिष्ट सेटिंग आइडेंटिफ़ायर से जुड़े मान (स्ट्रिंग के रूप में) लौटाती है।

// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...

// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...

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

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

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

इस समस्या को हल करने के लिए, मैंने निम्नानुसार टेम्पलेट विधि पैटर्न का उपयोग करने के बारे में सोचा था ।

public abstract class Setting
{
    protected abstract bool TryParseValues();

    protected abstract bool CheckValues();

    public abstract void SetDefaultValues();

    /// <summary>
    /// Template Method
    /// </summary>
    public bool TrySetValuesOrDefault()
    {
        if (!TryParseValues() || !CheckValues())
        {
            // parsing error or domain error
            SetDefaultValues();
            return false;
        }
        return true;
    }
}

public class RangeSetting : Setting
{
    private string minStr, maxStr;
    private byte min, max;

    public RangeSetting(string minStr, maxStr)
    {
        this.minStr = minStr;
        this.maxStr = maxStr;
    }

    protected override bool TryParseValues()
    {
        return (byte.TryParse(minStr, out min)
            && byte.TryParse(maxStr, out max));
    }

    protected override bool CheckValues()
    {
        return (0 < min && min < max);
    }

    public override void SetDefaultValues()
    {
        min = 5;
        max = 10;
    }
}

समस्या यह है कि इस तरह से हमें प्रत्येक सेटिंग के लिए, यहां तक ​​कि एक मूल्य के लिए एक नया वर्ग बनाने की आवश्यकता है। क्या इस तरह की समस्या के अन्य समाधान हैं?

संक्षेप में:

  1. आसान रखरखाव: उदाहरण के लिए, एक या अधिक मापदंडों का जोड़।
  2. एक्स्टेंसिबिलिटी: एप्लिकेशन का पहला संस्करण एकल कॉन्फ़िगरेशन फ़ाइल पढ़ सकता है, लेकिन बाद के संस्करण बहु-उपयोगकर्ता सेटअप की संभावना दे सकते हैं (व्यवस्थापक एक बुनियादी कॉन्फ़िगरेशन सेट करता है, उपयोगकर्ता केवल कुछ सेटिंग्स सेट कर सकते हैं, आदि)।
  3. वस्तु उन्मुख डिजाइन।

उन .properties फ़ाइल के उपयोग का प्रस्ताव रखने वालों के लिए, जहाँ आप फ़ाइल को विकास, परीक्षण और फिर उत्पादन के दौरान संग्रहीत करते हैं, क्योंकि यह उसी स्थान पर नहीं होगी, उम्मीद है। तब एप्लिकेशन को किसी भी स्थान (देव, परीक्षण या ठेस) के साथ फिर से कनेक्ट करना होगा जब तक कि आप रनटाइम पर पर्यावरण का पता नहीं लगा सकते हैं और फिर आपके ऐप के अंदर हार्डकोड किए गए स्थान हैं।

जवाबों:


8

अनिवार्य रूप से बाहरी कॉन्फ़िगरेशन फ़ाइल एक YAML दस्तावेज़ के रूप में एन्कोडेड है। इसके बाद आवेदन स्टार्ट अप के दौरान पार्स किया जाता है और एक कॉन्फ़िगरेशन ऑब्जेक्ट पर मैप किया जाता है।

अंतिम परिणाम मजबूत और प्रबंधन करने के लिए सभी सरल से ऊपर है।


7

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

कॉन्फ़िगरेशन API

खाका विधि पैटर्न बहुत सामान्य है, लेकिन मैं सवाल करता हूं कि क्या आपको वास्तव में उस सामान्यता की आवश्यकता है। आपको प्रत्येक प्रकार के कॉन्फ़िगरेशन मान के लिए एक वर्ग की आवश्यकता होगी । क्या आपके पास वास्तव में कई प्रकार हैं? मुझे लगता है कि आप एक मुट्ठी भर के साथ मिल सकता है: तार, ints, तैरता है, बुलियन, और enums। इनको देखते हुए, आपके पास एक ऐसा Configवर्ग हो सकता है, जिसके पास कुछ तरीके हैं:

int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)

(मुझे लगता है कि मुझे उस आखिरी अधिकार पर जेनरिक मिला है।)

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

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

भंडारण प्रारूप

जावा गुण फाइलें व्यक्तिगत कुंजी-मूल्य जोड़े को संग्रहीत करने के लिए ठीक लगती हैं, और वे उन मूल्यों के प्रकारों का समर्थन करते हैं जिन्हें मैंने ऊपर अच्छी तरह से वर्णित किया है। आप XML या JSON जैसे अन्य स्वरूपों पर भी विचार कर सकते हैं, लेकिन ये संभवतया ओवरकिल हैं जब तक कि आपके पास नेस्टेड या दोहराया डेटा नहीं है। उस बिंदु पर यह एक विन्यास फाइल से परे का रास्ता लगता है ....

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

यदि संभव हो तो मैं संपत्तियों के साथ रहना चाहता हूं।


2
अरे स्टुअर्ट, आपको यहाँ देखकर अच्छा लगा :-) मैं स्टुअर्ट के जवाब में जोड़ता हूं कि मुझे लगता है कि यदि आप जेनरिक का उपयोग दृढ़ता से टाइप करने के लिए करते हैं तो आपका टेम्प्लेट का विचार जावा में काम करेगा, इसलिए आप एक विकल्प के रूप में सेटिंग <टी> भी कर सकते थे।
मार्टिज़न वेरबर्ग

@StuartMarks: ठीक है, मेरा पहला विचार एक लिखने के लिए सिर्फ था Configवर्ग दृष्टिकोण आपके द्वारा प्रस्तावित और का उपयोग करें: getInt(), getByte(), getBoolean(), आदि .. सतत इस विचार के साथ, मैं पहले सभी मूल्यों पढ़ सकते हैं और मैं एक ध्वज को प्रत्येक मान से संबद्ध कर सकते (यह ध्वज असत्य है यदि डिसेरिएलाइज़ेशन के दौरान कोई समस्या हुई, उदाहरण के लिए पार्सिंग त्रुटियां)। उसके बाद, मैं सभी लोड किए गए मानों के लिए सत्यापन चरण शुरू कर सकता हूं और कोई भी डिफ़ॉल्ट मान सेट कर सकता हूं।
enzom83

2
मैं सभी विवरणों को सरल बनाने के लिए किसी प्रकार के JAXB या YAML दृष्टिकोण का पक्ष लूंगा।
गैरी रोवे

4

मैंने इसे कैसे किया है:

डिफ़ॉल्ट मानों के लिए सब कुछ प्रारंभ करें।

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


यह भी एक अच्छा विचार हो सकता है: एक वर्ग जो सेटिंग्स के मूल्यों को लोड करता है उसे कॉन्फ़िगरेशन फ़ाइल से मूल्यों को लोड करने के लिए केवल सौदा करना पड़ सकता है, अर्थात, इसकी जिम्मेदारी केवल मूल्यों को लोड करने के लिए एक हो सकती है कॉन्फ़िगरेशन फ़ाइल से; इसके बजाय प्रत्येक मॉड्यूल (जो कुछ सेटिंग्स का उपयोग करता है) पर मूल्यों को मान्य करने की जिम्मेदारी होगी।
enzom83

2

क्या इस तरह की समस्या के अन्य समाधान हैं?

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


1
पार्सिंग या रूपांतरणों के बारे में कोई गड़बड़ नहीं, कोई विन्यास तार के साथ चारों ओर कोई पेंच नहीं, कोई कास्टिंग कचरा नहीं। क्या मतलब?
enzom83

1
मेरा मतलब है कि: 1. आपको AppConfig परिणाम (एक स्ट्रिंग) लेने की जरूरत नहीं है और इसे आप जो चाहते हैं उसमें पार्स करें। 2. आपको किसी भी प्रकार के स्ट्रिंग को निर्दिष्ट करने की आवश्यकता नहीं है जिसे आप चाहते हैं कि कौन सा पैरामीटर कॉन्फ़िगर करना है; यह उन चीजों में से एक है जो मानवीय त्रुटि और रिफ्लेक्टर के लिए कठिन हैं और 3. आपको प्रोग्राम को व्यावहारिक रूप से सेट करने के लिए अन्य प्रकार के रूपांतरण करने की आवश्यकता नहीं है।
तेलस्टिन

2

कम से कम .NET में, आप बहुत आसानी से अपने स्वयं के दृढ़ता से टाइप किए गए कॉन्फ़िगरेशन ऑब्जेक्ट बना सकते हैं - एक त्वरित उदाहरण के लिए इस MSDN लेख को देखें ।

Protip: अपने config class को एक इंटरफ़ेस में लपेटें और अपने एप्लिकेशन को उस पर बात करने दें। परीक्षणों या लाभ के लिए नकली कॉन्फ़िगरेशन को इंजेक्ट करना आसान बनाता है।


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

1
मैं आमतौर पर तर्क देता हूं कि निम्न स्तर के कॉन्फ़िगरेशन सत्यापन के लिए कुछ नहीं है - मैं कोड में इसे कवर करने के लिए अपने कॉन्फ़िगरेशन वर्ग में एक AssertConfigrationIsValid विधि जोड़ूंगा। यदि वह आपके लिए काम नहीं करता है, तो मुझे लगता है कि आप विशेषता के आधार वर्ग का विस्तार करके अपना कॉन्फ़िगरेशन सत्यापनकर्ता बना सकते हैं। उनके पास एक तुलना सत्यापनकर्ता है, इसलिए वे स्पष्ट रूप से क्रॉस-प्रॉपर्टी से बात कर सकते हैं।
व्याट बार्नेट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.