जावा में त्रुटि कोड / स्ट्रिंग्स को परिभाषित करने का सबसे अच्छा तरीका?


118

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

// Example: errorCode = 1, 
//          errorString = "There was a problem accessing the database."
throw new SomeWebServiceException(errorCode, errorString);

क्लाइंट प्रोग्राम को संदेश दिखाया जा सकता है:

"त्रुटि # 1 में सुधार हुआ है: डेटाबेस तक पहुँचने में समस्या थी।"

मेरा पहला विचार Enumत्रुटि कोड का उपयोग करना था और toStringत्रुटि स्ट्रिंग को वापस करने के तरीकों को ओवरराइड करना था । यहां वह है जो मैंने जुटाया:

public enum Errors {
  DATABASE {
    @Override
    public String toString() {
      return "A database error has occured.";
    }
  },

  DUPLICATE_USER {
    @Override
    public String toString() {
      return "This user already exists.";
    }
  },

  // more errors follow
}

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


देर से टिप्पणी लेकिन एक उल्लेख के लायक मैं बात ... 1) क्या आपको अपवाद में वास्तव में त्रुटि कोड की आवश्यकता है? नीचे blabla999 उत्तर देखें। 2) आपको उपयोगकर्ता को बहुत अधिक त्रुटि जानकारी देने से सावधान रहना चाहिए। उपयोगी त्रुटि जानकारी सर्वर लॉग को लिखी जानी चाहिए, लेकिन क्लाइंट को नंगे न्यूनतम (उदाहरण के लिए "कोई समस्या नहीं थी")। यह सुरक्षा का सवाल है और स्पूफर्स को पैर जमाने से रोक रहा है।
wmorrison365

जवाबों:


161

वैसे एनम समाधान का एक बेहतर कार्यान्वयन निश्चित रूप से है (जो आमतौर पर काफी अच्छा है):

public enum Error {
  DATABASE(0, "A database error has occured."),
  DUPLICATE_USER(1, "This user already exists.");

  private final int code;
  private final String description;

  private Error(int code, String description) {
    this.code = code;
    this.description = description;
  }

  public String getDescription() {
     return description;
  }

  public int getCode() {
     return code;
  }

  @Override
  public String toString() {
    return code + ": " + description;
  }
}

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

यह मत भूलो कि यह अंतर्राष्ट्रीयकरण बिल्कुल भी नहीं है - लेकिन जब तक आपका वेब सेवा क्लाइंट आपको एक स्थानीय विवरण नहीं भेजता है, तब तक आप इसे आसानी से वैसे भी अंतर्राष्ट्रीय नहीं कर सकते। कम से कम उनके पास क्लाइंट पक्ष पर i18n के लिए उपयोग करने के लिए त्रुटि कोड होगा ...


13
अंतर्राष्ट्रीयकरण करने के लिए, वर्णन फ़ील्ड को एक स्ट्रिंग कोड के साथ बदलें जिसे संसाधन बंडल में देखा जा सकता है?
मार्कस डाउनिंग

@ मारकस: मुझे वह विचार पसंद है। मैं इस बात को दरवाजे से बाहर करने पर ध्यान केंद्रित कर रहा हूं, लेकिन जब हम अंतर्राष्ट्रीयकरण को देखते हैं, तो मुझे लगता है कि मैं वही करूंगा जो आपने सुझाया था। धन्यवाद!
विलियम ब्रेंडेल

@marcus, अगर toString () को ओवरराइड नहीं किया गया है (जिसे इसकी आवश्यकता नहीं है), तो स्ट्रिंग कोड बस enum मान toString () हो सकता है जो इस मामले में DATABASE, या DUPLICK_USER होगा।
रूबल

@ जोंन स्कीट! मुझे यह समाधान पसंद है, कोई ऐसा समाधान कैसे तैयार कर सकता है जो स्थानीयकरण करना आसान है (या अन्य भाषाओं में अनुवाद करना आदि) एंड्रॉइड में इसका उपयोग करने के बारे में सोचकर मैं वहां कठोर कोडिंग स्ट्रिंग्स के बजाय R.string.IDS_XXXX का उपयोग कर सकता हूं?
एबी

1
@AB: अच्छी तरह से एनम मिल जाने के बाद, आप आसानी से प्रॉपर्टी फाइल या जो भी हो, एनम वैल्यू से संबंधित स्थानीयकृत संसाधन को निकालने के लिए एक वर्ग लिख सकते हैं।
जॉन स्कीट

34

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

मेरी परियोजनाओं पर, आम तौर पर मेरे पास एक इंटरफ़ेस होता है जिसमें त्रुटि कोड होते हैं (स्ट्रिंग या पूर्णांक, यह बहुत परवाह नहीं करता है), जिसमें इस त्रुटि के लिए गुण फ़ाइलों में कुंजी है:

public interface ErrorCodes {
    String DATABASE_ERROR = "DATABASE_ERROR";
    String DUPLICATE_USER = "DUPLICATE_USER";
    ...
}

गुण फ़ाइल में:

DATABASE_ERROR=An error occurred in the database.
DUPLICATE_USER=The user already exists.
...

आपके समाधान के साथ एक और समस्या मुख्यता है: आपके पास केवल 2 त्रुटियां हैं, और पहले से ही कोड की 12 लाइनें हैं। तो अपनी गणना फ़ाइल की कल्पना करें जब आपके पास प्रबंधित करने के लिए सैकड़ों त्रुटियां होंगी!


2
अगर मैं ऐसा कर सकता हूं तो मैं इसे 1 से अधिक करूंगा। स्ट्रिंग्स को हार्डकोड करना रखरखाव के लिए बदसूरत है।
रॉबिन

3
इंटरफ़ेस में स्ट्रिंग स्थिरांक खराब आइडिया है। आप निजी कंस्ट्रक्टर, प्रति पैकेज या संबंधित क्षेत्र के साथ अंतिम कक्षाओं में एनम का उपयोग कर सकते हैं या स्ट्रिंग स्थिरांक का उपयोग कर सकते हैं। जॉन स्कैट्स का जवाब Enums के साथ दें। कृपया जांचें। stackoverflow.com/questions/320588/…
आनंद वर्की फिलिप्स

21

ओवरलोडिंग toString () थोड़ा सा icky लगता है - जो कि toString () के सामान्य उपयोग का थोड़ा खिंचाव लगता है।

व्हाट अबाउट:

public enum Errors {
  DATABASE(1, "A database error has occured."),
  DUPLICATE_USER(5007, "This user already exists.");
  //... add more cases here ...

  private final int id;
  private final String message;

  Errors(int id, String message) {
     this.id = id;
     this.message = message;
  }

  public int getId() { return id; }
  public String getMessage() { return message; }
}

मुझे बहुत सफाई लगती है ... और कम वाचालता।


5
किसी भी ऑब्जेक्ट पर ओवरस्ट्रीमिंग () को लोड करना (अकेले दुश्मनी छोड़ना) काफी सामान्य है।
क्लेटस

जो जॉन स्कीट के समाधान के रूप में काफी लचीला नहीं है, लेकिन यह अभी भी समस्या को हल करता है। धन्यवाद!
विलियम ब्रेंडेल

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

1
मैं कोवान के साथ सहमत हूँ, इस तरह से () का उपयोग करते हुए थोड़ा सा 'हैकिश' लगता है। हिरन के लिए बस एक तेज धमाका और सामान्य उपयोग नहीं। Enum के लिए, toString () को enum के नाम को स्थिर रखना चाहिए। जब आप किसी वैरिएबल का मान चाहते हैं तो यह डिबगर में दिलचस्प लगेगा।
रॉबिन

19

अपनी आखिरी नौकरी में मैं एनम संस्करण में थोड़ा गहरा गया:

public enum Messages {
    @Error
    @Text("You can''t put a {0} in a {1}")
    XYZ00001_CONTAINMENT_NOT_ALLOWED,
    ...
}

@Error, @Info, @Warning को क्लास फाइल में रखा गया है और रनटाइम पर उपलब्ध है। (हमारे पास संदेश वितरण के रूप में अच्छी तरह से वर्णन करने में मदद करने के लिए कुछ अन्य एनोटेशन थे)

@ टेक्स्ट एक संकलन-टाइम एनोटेशन है।

मैंने इसके लिए एक एनोटेशन प्रोसेसर लिखा है जो निम्न कार्य करता है:

  • सत्यापित करें कि कोई डुप्लिकेट संदेश संख्याएँ नहीं हैं (पहले अंडरस्कोर से पहले का भाग)
  • मैसेज टेक्स्ट को सिंटैक्स-चेक करें
  • एक संदेश उत्पन्न करें। फाइल में वह पाठ है, जिसमें एनम वैल्यू के द्वारा लिखा गया है।

मैंने कुछ उपयोगिताएँ लिखीं जो लॉग त्रुटियों में मदद करती हैं, उन्हें अपवाद के रूप में लपेटें (यदि वांछित हो) और आगे।

मैं उन्हें ओपन-सोर्स करने के लिए उन्हें लेने की कोशिश कर रहा हूं ... - स्कॉट


त्रुटि संदेशों को संभालने का अच्छा तरीका। क्या आपने इसे पहले ही खोल दिया था?
bobbel

5

मेरा सुझाव है कि आप java.util.ResourceBundle पर एक नज़र डालें। आपको I18N के बारे में परवाह करनी चाहिए, लेकिन अगर आप ऐसा नहीं करते हैं, तो भी यह इसके लायक है। संदेशों को बाहरी बनाना एक बहुत अच्छा विचार है। मैंने पाया है कि व्यावसायिक लोगों को एक स्प्रेडशीट देने में सक्षम होना उपयोगी था जो उन्हें सटीक भाषा में डालने की अनुमति देता था जिसे वे देखना चाहते थे। हमने संकलन समय पर .properties फ़ाइलों को उत्पन्न करने के लिए एक चींटी कार्य लिखा था। यह I18N को तुच्छ बनाता है।

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


4

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


3

इसे हल करने के कई तरीके हैं। मेरा पसंदीदा तरीका इंटरफेस है:

public interface ICode {
     /*your preferred code type here, can be int or string or whatever*/ id();
}

public interface IMessage {
    ICode code();
}

अब आप किसी भी संख्या को परिभाषित कर सकते हैं जो संदेश प्रदान करते हैं:

public enum DatabaseMessage implements IMessage {
     CONNECTION_FAILURE(DatabaseCode.CONNECTION_FAILURE, ...);
}

अब आपके पास स्ट्रिंग्स में बदलने के लिए कई विकल्प हैं। आप स्ट्रिंग्स को अपने कोड (एनोटेशन या एनम कंस्ट्रक्टर पैरामीटर का उपयोग करके) में संकलित कर सकते हैं या आप उन्हें एक कॉन्फ़िगरेशन / प्रॉपर्टी फ़ाइल या डेटाबेस टेबल या मिश्रण से पढ़ सकते हैं। उत्तरार्द्ध मेरा पसंदीदा तरीका है क्योंकि आपको हमेशा कुछ संदेशों की आवश्यकता होगी जिन्हें आप बहुत जल्दी पाठ में बदल सकते हैं (यानी जब आप डेटाबेस से कनेक्ट होते हैं या कॉन्फ़िगरेशन पढ़ते हैं)।

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

उन फ्रेमवर्क का उपयोग करना जो जावा को पार्स कर सकते हैं जैसे https://github.com/javaparser/javaparser या ग्रहण से एक , आप यह भी देख सकते हैं कि एनम कहाँ उपयोग किए जाते हैं और अप्रयुक्त पाते हैं।


2

मैं और मेरी कंपनी में हमारी टीम के बाकी) त्रुटि कोड वापस करने के बजाय अपवाद उठाना पसंद करते हैं। त्रुटि कोड को हर जगह जांचा जाना चाहिए, चारों ओर पारित किया गया, और कोड की मात्रा बड़ी होने पर कोड को अपठनीय बनाने की प्रवृत्ति होती है।

त्रुटि वर्ग तब संदेश को परिभाषित करेगा।

पुनश्च: और वास्तव में अंतर्राष्ट्रीयकरण के लिए भी परवाह है!
PPS: आप यदि आवश्यक हो तो कम-से-कम वातावरण, जहां एक्सेप्शन क्लासेस और दोस्त विस्तार योग्य / परिवर्तनशील हों, वहां पर उठने-बैठने की विधि को फिर से परिभाषित और जोड़ सकते हैं, फ़िल्टर कर सकते हैं)


क्षमा करें, रॉबिन, लेकिन फिर (कम से कम उपरोक्त उदाहरण से), ये दो अपवाद होने चाहिए - "डेटाबेस त्रुटि" और "डुप्लिकेट उपयोगकर्ता" पूरी तरह से अलग हैं कि दो अलग-अलग त्रुटि-उपवर्ग बनाए जाने चाहिए, जो व्यक्तिगत रूप से उपलब्ध हैं ( एक प्रणाली होने के नाते, दूसरा एक व्यवस्थापक त्रुटि होने के कारण)
blabla999

और एक या दूसरे अपवाद के बीच अंतर न करने के लिए, त्रुटि कोड का क्या उपयोग किया जाता है? तो कम से कम हैंडलर के ऊपर, वह ठीक यही है: त्रुटि-कोड से निपटना जो कि पास हो जाता है और यदि स्विच-ऑन होता है।
ब्लाबला 999

मुझे लगता है कि अपवाद का नाम एक त्रुटि कोड की तुलना में कहीं अधिक चित्रण और आत्म-वर्णन होगा। बेहतर अपवाद नामों की खोज में अधिक विचार रखने के लिए बेहतर, आईएमओ।
duffymo

@ blabla999 आह, बिल्कुल मेरे विचार। एक मोटे-अनाज वाले अपवाद को क्यों पकड़ें और फिर "अगर एररकोड == x, या y, या z" का परीक्षण करें। ऐसा दर्द और दाने के खिलाफ जाता है। फिर, भी, आप अपने स्टैक में विभिन्न स्तरों पर अलग-अलग अपवादों को नहीं पकड़ सकते। आपको प्रत्येक स्तर पर पकड़ना होगा और प्रत्येक में त्रुटि कोड का परीक्षण करना होगा। यह क्लाइंट कोड को इतना अधिक वर्बोज़ बनाता है ... अगर मैं कर सकता था तो +1 + अधिक। उन्होंने कहा, मुझे लगता है कि हमें ओपी के सवाल का जवाब देना होगा।
wmorrison365

2
ध्यान रखें, यह एक वेब सेवा के लिए है। ग्राहक केवल तार पार्स कर सकता है। सर्वर की ओर से अभी भी ऐसे अपवाद होंगे जो एक त्रुटि कोड सदस्य हैं, जिसका उपयोग क्लाइंट की अंतिम प्रतिक्रिया में किया जा सकता है।
पर्किश

1

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

public enum Error {
    DATABASE(0, "A database error has occured. "), 
    DUPLICATE_USER(1, "User already exists. ");
    ....
    private String description = "";
    public Error changeDescription(String description) {
        this.description = description;
        return this;
    }
    ....
}

Error genericError = Error.DATABASE;
Error specific = Error.DUPLICATE_USER.changeDescription("(Call Admin)");

संपादित करें: ठीक है, यहाँ पर एनम का उपयोग करना थोड़ा खतरनाक है क्योंकि आप विशेष रूप से एनम को स्थायी रूप से बदलते हैं। मुझे लगता है कि बेहतर होगा कि आप क्लास में बदलाव करें और स्टेटिक फील्ड्स का इस्तेमाल करें, लेकिन अब आप '==' का इस्तेमाल नहीं कर सकते। तो मुझे लगता है कि यह एक अच्छा उदाहरण है कि क्या नहीं करना है, (या इसे केवल आरंभीकरण के दौरान) :)


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

0

त्रुटि कोड / संदेश की परिभाषा के लिए enum अभी भी एक अच्छा समाधान है, हालांकि इसमें i18n चिंताएं हैं। वास्तव में हमारे पास दो स्थितियां हो सकती हैं: कोड / संदेश अंतिम उपयोगकर्ता या सिस्टम इंटीग्रेटर को प्रदर्शित किया जाता है। बाद के मामले के लिए, I18N आवश्यक नहीं है। मुझे लगता है कि वेब सेवाओं के बाद के मामले में सबसे अधिक संभावना है।


0

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

इसका समर्थन करने के लिए आपको हमेशा के लिए लॉक कर दिया जाएगा, क्योंकि स्ट्रिंग प्रारूप में परिवर्तन क्लाइंट प्रोग्राम को तोड़ देगा / कर सकता है।


0

कृपया नीचे दिए गए उदाहरण का अनुसरण करें:

public enum ErrorCodes {
NO_File("No file found. "),
private ErrorCodes(String value) { 
    this.errordesc = value; 
    }
private String errordesc = ""; 
public String errordesc() {
    return errordesc;
}
public void setValue(String errordesc) {
    this.errordesc = errordesc;
}

};

अपने कोड में इसे कॉल करें:

fileResponse.setErrorCode(ErrorCodes.NO_FILE.errordesc());

0

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

PropertyResourceBundle के बारे में अधिक जानकारी के लिए जावा डॉक्टर को देखें

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