क्या एक स्ट्रिंग स्थिरांक को परिभाषित किया जाना चाहिए अगर यह केवल एक बार उपयोग होने वाला है?


24

हम जैक्सन (जावा के लिए एक XPath पुस्तकालय) के लिए एक एडेप्टर लागू कर रहे हैं जो हमें अपने आवेदन के डेटा मॉडल तक पहुंचने के लिए XPath का उपयोग करने की अनुमति देता है।

यह उन वर्गों को लागू करने के द्वारा किया जाता है जो हमारे डेटा मॉडल के तत्वों में स्ट्रिंग्स (जैक्सन से हमारे पास) को मैप करते हैं। हमारा अनुमान है कि हमें कुल मिलाकर 1000 से अधिक स्ट्रिंग तुलनाओं के साथ लगभग 100 वर्गों की आवश्यकता होगी।

मुझे लगता है कि ऐसा करने का सबसे अच्छा तरीका सरल है अगर / अन्यथा कोड में सीधे लिखे गए तार के साथ बयान - प्रत्येक स्ट्रिंग्स को एक स्थिर के रूप में परिभाषित करने के बजाय। उदाहरण के लिए:

public Object getNode(String name) {
    if ("name".equals(name)) {
        return contact.getFullName();
    } else if ("title".equals(name)) {
        return contact.getTitle();
    } else if ("first_name".equals(name)) {
        return contact.getFirstName();
    } else if ("last_name".equals(name)) {
        return contact.getLastName();
    ...

हालाँकि मुझे हमेशा सिखाया गया था कि हमें स्ट्रिंग मानों को सीधे कोड में एम्बेड नहीं करना चाहिए, बल्कि इसके बजाय स्ट्रिंग स्थिरांक बनाएं। यह कुछ इस तरह दिखेगा:

private static final String NAME = "name";
private static final String TITLE = "title";
private static final String FIRST_NAME = "first_name";
private static final String LAST_NAME = "last_name";

public Object getNode(String name) {
    if (NAME.equals(name)) {
        return contact.getFullName();
    } else if (TITLE.equals(name)) {
        return contact.getTitle();
    } else if (FIRST_NAME.equals(name)) {
        return contact.getFirstName();
    } else if (LAST_NAME.equals(name)) {
        return contact.getLastName();
    ...

इस मामले में मुझे लगता है कि यह एक बुरा विचार है। निरंतर का उपयोग केवल एक बार किया जाएगा, getNode()विधि में। स्ट्रिंग्स का सीधे उपयोग करना, स्थिरांक का उपयोग करने के रूप में पढ़ना और समझना आसान है, और कोड की कम से कम एक हजार पंक्तियों को लिखने से हमें बचाता है।

तो क्या एकल उपयोग के लिए स्ट्रिंग स्थिरांक को परिभाषित करने का कोई कारण है? या सीधे तार का उपयोग करना स्वीकार्य है?


पुनश्च। इससे पहले कि कोई भी इसके बजाय एनम का उपयोग करने का सुझाव देता है, हमने इसे बदल दिया, लेकिन एनम रूपांतरण साधारण स्ट्रिंग तुलना की तुलना में 15 गुना धीमा है, इसलिए इसे माना नहीं जा रहा है।


निष्कर्ष: नीचे दिए गए उत्तरों ने इस प्रश्न के दायरे को केवल स्ट्रिंग स्थिरांक से आगे बढ़ाया, इसलिए मेरे दो निष्कर्ष हैं:

  • इस परिदृश्य में स्ट्रिंग स्थिरांक के बजाय स्ट्रिंग्स का उपयोग करना शायद ठीक है, लेकिन
  • वहाँ तार का उपयोग करने से बचने के तरीके हैं, जो बेहतर हो सकता है।

इसलिए मैं रैपर तकनीक की कोशिश करने जा रहा हूं जो पूरी तरह से तार से बचा जाता है। दुर्भाग्य से हम स्ट्रिंग स्विच स्टेटमेंट का उपयोग नहीं कर सकते क्योंकि हम अभी जावा 7 पर नहीं हैं। अंततः, हालांकि, मुझे लगता है कि हमारे लिए सबसे अच्छा जवाब प्रत्येक तकनीक की कोशिश करना और उसके प्रदर्शन का मूल्यांकन करना है। वास्तविकता यह है कि अगर एक तकनीक स्पष्ट रूप से तेज है, तो हम शायद इसकी सुंदरता या परंपरा के पालन की परवाह किए बिना इसे चुनेंगे।


3
यदि आप स्टेटमेंट्स करते हैं तो आप मैन्युअल रूप से 1000 कीबोर्ड की योजना नहीं बनाते हैं?
जेफ़ो

1
मुझे यह बहुत दुखद लगता है कि यह सरल कुछ भाषाओं में कितना अप्रिय हो सकता है ...
जॉन पर्पल

5
Java 7 स्ट्रिंग्स को switchलेबल के रूप में अनुमति देता है । ifकैस्केड के बजाय एक स्विच का उपयोग करें ।
मोनिका को पुनः स्थापित करें - एम। श्रोडर

3
यदि आप किसी स्ट्रिंग को उसके एनम मान में बदलते हैं, तो एनम रूपांतरण 15 गुना धीमा है! सीधे एनम पास करें और उसी प्रकार के एक और एनम मूल्य के साथ तुलना करें!
नील

2
HashMap जैसी गंध एक समाधान हो सकता है।
मारियोड्स

जवाबों:


5

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

दूसरे शब्दों में, पास "नाम" के बजाय, आप "fullname" पास करेंगे क्योंकि प्राप्त विधि का नाम "getFullName ()" है।

Map<String, Method> methodMapping = null;

public Object getNode(String name) {
    Map<String, Method> methods = getMethodMapping(contact.getClass());
    return methods.get(name).invoke(contact);
}

public Map<String, Method> getMethodMapping(Class<?> contact) {
    if(methodMapping == null) {
        Map<String, Method> mapping = new HashMap<String, Method>();
        Method[] methods = contact.getDeclaredMethods();
        for(Method method : methods) {
            if(method.getParameterTypes().length() == 0) {
                if(method.getName().startsWith("get")) {
                    mapping.put(method.getName().substring(3).toLower(), method);
                } else if (method.getName().startsWith("is"))) {
                    mapping.put(method.getName().substring(2).toLower(), method);
                }
            }
        }
        methodMapping = mapping;
    }
    return methodMapping;
}

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

public class ContactWrapper {
    private Contact contact;

    public ContactWrapper(Contact contact) {
        this.contact = contact;
    }

    public String getFullName() {
        return contact.getFullName();
    }
    ...
}

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


मुझे एक रैपर ऑब्जेक्ट का विचार पसंद है, जिसके माध्यम से बुलाया जाता है .invoke(), क्योंकि यह पूरी तरह से स्ट्रिंग स्थिरांक के साथ दूर करता है। मैं मानचित्र को स्थापित करने के लिए रनटाइम रिफ्लेक्शन के लिए उत्सुक नहीं हूं, हालांकि शायद getMethodMapping()एक staticब्लॉक में निष्पादित करना ठीक होगा ताकि सिस्टम चालू होने के बजाय स्टार्टअप पर ऐसा हो।
गच

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

@ नील अपाचे कॉमन्स से बीन यूटिल्स का उपयोग क्यों नहीं करते? यह एम्बेडेड ऑब्जेक्ट्स का भी समर्थन करता है। आप पूरे डेटा संरचना के माध्यम से जा सकते हैं obj.attrA.attrB.attrN और इसमें कई अन्य
कब्जे हैं

मैप्स के साथ मैपिंग के बजाय मैं @ नोटों के लिए जाऊंगा। जेपीए जैसा कुछ करता है। एक विशिष्ट attr या getter के साथ नियंत्रक प्रविष्टियों (स्ट्रिंग) मानचित्रण के लिए मेरे अपने एनोटेशन को परिभाषित करने के लिए। एनोटेशन के साथ काम करना काफी आसान है और यह जावा 1.6 (मुझे लगता है) से उपलब्ध है
Laiv

5

यदि संभव हो तो जावा 7 का उपयोग करें जो आपको switchस्टेटमेंट में स्ट्रिंग्स का उपयोग करने की अनुमति देता है ।

से http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html

public class StringSwitchDemo {

    public static int getMonthNumber(String month) {

        int monthNumber = 0;

        if (month == null) {
            return monthNumber;
        }

        switch (month.toLowerCase()) {
            case "january":
                monthNumber = 1;
                break;
            case "february":
                monthNumber = 2;
                break;
            case "march":
                monthNumber = 3;
                break;
            case "april":
                monthNumber = 4;
                break;
            case "may":
                monthNumber = 5;
                break;
            case "june":
                monthNumber = 6;
                break;
            case "july":
                monthNumber = 7;
                break;
            case "august":
                monthNumber = 8;
                break;
            case "september":
                monthNumber = 9;
                break;
            case "october":
                monthNumber = 10;
                break;
            case "november":
                monthNumber = 11;
                break;
            case "december":
                monthNumber = 12;
                break;
            default: 
                monthNumber = 0;
                break;
        }

        return monthNumber;
    }

    public static void main(String[] args) {

        String month = "August";

        int returnedMonthNumber =
            StringSwitchDemo.getMonthNumber(month);

        if (returnedMonthNumber == 0) {
            System.out.println("Invalid month");
        } else {
            System.out.println(returnedMonthNumber);
        }
    }
}

मैंने मापा नहीं है, लेकिन मेरा मानना ​​है कि स्विच स्टेटमेंट तुलना की लंबी सूची के बजाय जंप टेबल पर संकलित होते हैं। यह और भी तेज होना चाहिए।

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


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

एक बहुत अच्छा बिंदु; अगर यह अच्छा प्रदर्शन करता है तो यह सिर्फ इसके लिए जावा 7 में जाने लायक हो सकता है ...
gutch

4

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


हमने मौजूदा तरीकों की व्याख्या करने पर विचार किया, लेकिन कुछ मैपिंग ने कई ऑब्जेक्ट्स (उदाहरण के लिए "देश" मैपिंग object.getAddress().getCountry()) को पार कर लिया, जो एनोटेशन के साथ प्रस्तुत करना मुश्किल है। अगर / और स्ट्रिंग तुलना बहुत सुंदर नहीं हैं, लेकिन वे तेज, लचीले, समझने में आसान और यूनिट टेस्ट में आसान हैं।
gutch

1
आप टाइपोस और त्रुटियों की क्षमता के बारे में सही हैं; मेरी एकमात्र रक्षा इकाई परीक्षण है। बेशक इसका मतलब है कि अभी और कोड ...
gutch

2

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


मैं सहमत हूं, हालांकि, यदि यह कोड स्वचालित रूप से उत्पन्न हो जाएगा और स्थिरांक का उपयोग कहीं और नहीं किया जाता है, तो इससे कोई फर्क नहीं पड़ता (ओपी का कहना है कि उन्हें 100 वर्गों में ऐसा करने की आवश्यकता है)।
NoChance

5
मैं सिर्फ "मेंटेनेंस एंगल" नहीं देखता हूँ, यहाँ आप "first_name" को "दिए गए" में बदल देते हैं, दोनों में से किसी एक मामले में एक बार। हालाँकि नामांकित स्थिरांक के मामले में अब आप एक गंदे चर "first_name" के साथ रह गए हैं, जो संदर्भित करता है एक स्ट्रिंग "दिया गया" इसलिए आप शायद इसे बदलना चाहते हैं और साथ ही साथ अब आपको दो स्थानों पर तीन बदलाव करने हैं
जेम्स एंडरसन

1
सही IDE के साथ, ये परिवर्तन तुच्छ हैं। मैं जो वकालत कर रहा हूं, वह यह स्पष्ट है कि इन परिवर्तनों को करना कहां है क्योंकि आपने कक्षा के शीर्ष पर स्थिरांक घोषित करने के लिए समय लिया है और कक्षा में बाकी कोड के माध्यम से पढ़ने के लिए नहीं है ये बदलाव करें।
बर्नार्ड

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

1
शायद, लेकिन यही कारण है कि मैं अपने स्थिरांक को अच्छी तरह से बताता हूं।
बर्नार्ड

1

यदि आपका नामकरण सुसंगत है (उर्फ "some_whatever"हमेशा मैप किया जाता है getSomeWhatever()) तो आप गेट विधि को निर्धारित करने और निष्पादित करने के लिए प्रतिबिंब का उपयोग कर सकते हैं।


बेहतर getSome_whatever ()। ऊंट के मामले को तोड़ना हो सकता है, लेकिन यह सुनिश्चित करना अधिक महत्वपूर्ण है कि प्रतिबिंब काम करता है। इसके अलावा इसका अतिरिक्त फायदा है कि यह आपको कहता है, "बिल्ली ने हमें ऐसा क्यों किया है .. ओह रुको .. रुको! जॉर्ज उस पद्धति का नाम नहीं बदलते हैं!"
नील

0

मुझे लगता है कि, एनोटेशन प्रसंस्करण समाधान हो सकता है, यहां तक ​​कि एनोटेशन के बिना भी। यह वह चीज है जो आपके लिए सभी उबाऊ कोड उत्पन्न कर सकती है। नकारात्मक पक्ष यह है कि आपको एन मॉडल कक्षाओं के लिए एन उत्पन्न कक्षाएं मिलेंगी। आप मौजूदा कक्षा में कुछ भी नहीं जोड़ सकते हैं, लेकिन कुछ ऐसा लिख ​​सकते हैं

public Object getNode(String name) {
    return SomeModelClassHelper.getNode(this, name);
}

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

public Object getNode(String name) {
    return getHelper(getClass()).getNode(this, name);
}

एक सुपरक्लास में।


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


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

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