2008-12-05 16 views
7

मुझे यह जानने में दिलचस्पी है कि आप किस तकनीक का उपयोग किसी ऑब्जेक्ट के दौरान किसी ऑब्जेक्ट की आंतरिक स्थिति को सत्यापित करने के लिए कर रहे हैं, जो कि अपने स्वयं के दृष्टिकोण से, केवल खराब आंतरिक स्थिति या आविष्कार उल्लंघन के कारण विफल हो सकता है।आप ऑब्जेक्ट की आंतरिक स्थिति को कैसे सत्यापित करते हैं?

मेरे प्राथमिक ध्यान, सी पर ++ है के बाद से सी # में सरकारी और प्रचलित तरीका एक अपवाद फेंकने के लिए है, और C++ सिर्फ एक एकल तरीका यह (ठीक है, वास्तव में नहीं सी # में या तो, मुझे पता है क्या करना नहीं है उस)।

ध्यान दें कि मैं फ़ंक्शन पैरामीटर सत्यापन के बारे में बात नहीं कर रहा हूं, लेकिन क्लास इनवेरिएंट अखंडता जांच की तरह अधिक है।

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

लेकिन Printer ऑब्जेक्ट पर, आंतरिक स्थिति खराब होने पर यह ऑपरेशन विफल हो सकता है, यानी, क्लास इनवेरिएंट टूटा हुआ है, जिसका मूल रूप से अर्थ है: एक बग। यह स्थिति Printer ऑब्जेक्ट के उपयोगकर्ता के लिए किसी भी ब्याज की जरूरी नहीं है।

व्यक्तिगत रूप से, मैं आंतरिक राज्य सत्यापन की तीन शैलियों को मिश्रित करता हूं और मैं वास्तव में यह तय नहीं कर सकता कि कौन सा सबसे अच्छा है, यदि कोई है, तो केवल कौन सा सबसे खराब है। मैं इन पर आपके विचार सुनना चाहता हूं और यह भी कि आप इस मामले पर अपने किसी भी अनुभव और विचार साझा करते हैं।

पहली शैली मैं का उपयोग करें - बेहतर भ्रष्ट डेटा की तुलना में चलाया तरह से असफल: -:

void Printer::Queue(const PrintJob& job) 
{ 
    // Validate the state in debug builds only. 
    // Break into the debugger in debug builds. 
    // Always proceed with the queuing, also in a bad state. 
    DebugAssert(IsValidState()); 

    // Continue with queuing, parameter checking, etc. 
    // Generally, behavior is now undefined, because of bad internal state. 
    // But, specifically, this often means an access violation when 
    // a NULL pointer is dereferenced, or something similar, and that crash will 
    // generate a dump file that can be used to find the error cause during 
    // testing before shipping the product. 
} 

तीसरे शैली बेहतर दुर्घटना भ्रष्ट डेटा की तुलना में बेकाबू

void Printer::Queue(const PrintJob& job) 
{ 
    // Validate the state in both release and debug builds. 
    // Never proceed with the queuing in a bad state. 
    if(!IsValidState()) 
    { 
     throw InvalidOperationException(); 
    } 

    // Continue with queuing, parameter checking, etc. 
    // Internal state is guaranteed to be good. 
} 

दूसरी शैली मैं का उपयोग मैं उपयोग करता हूं - भ्रष्ट डेटा से बेहतर चुपचाप और रक्षात्मक रूप से जमानत:

void Printer::Queue(const PrintJob& job) 
{ 
    // Validate the state in both release and debug builds. 
    // Break into the debugger in debug builds. 
    // Never proceed with the queuing in a bad state. 
    // This object will likely never again succeed in queuing anything. 
    if(!IsValidState()) 
    { 
     DebugBreak(); 
     return; 
    } 

    // Continue with defenestration. 
    // Internal state is guaranteed to be good. 
} 

मेरा कमेन शैलियों के लिए टीएस:

  1. मुझे लगता है कि मैं दूसरी शैली पसंद करता हूं, जहां विफलता छिपी नहीं है, बशर्ते कि एक प्रवेश उल्लंघन वास्तव में एक दुर्घटना का कारण बनता है।
  2. यदि यह आविष्कार में शामिल एक पूर्ण सूचक नहीं है, तो मैं पहली शैली की तरफ झुकता हूं।
  3. मैं वास्तव में तीसरी शैली को नापसंद करता हूं, क्योंकि यह बहुत सारी बग छुपाएगा, लेकिन मैं उन लोगों को जानता हूं जो इसे उत्पादन कोड में पसंद करते हैं, क्योंकि यह एक मजबूत सॉफ़्टवेयर का भ्रम पैदा करता है जो क्रैश नहीं होता है (विशेषताएं केवल काम करने के लिए रुक जाएंगी , Printer ऑब्जेक्ट पर कतार में कतार में)।

क्या आप इनमें से कोई भी पसंद करते हैं या आपके पास इसे प्राप्त करने के अन्य तरीके हैं?

उत्तर

2

प्रश्न आपके सॉफ्टवेयर का परीक्षण करने के तरीके के साथ संयोजन में सबसे अच्छा माना जाता है।

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

अपनी शैली 3 की तरह रक्षात्मक कोड जोड़ने के लिए उपयुक्त हो सकता है: आपके DebugBreak परीक्षण निर्माण में निदान को डंप करेगा, लेकिन डेवलपर्स के लिए ब्रेक पॉइंट बनें। इससे ऐसी स्थिति कम हो जाती है जहां एक डेवलपर को असंबंधित कोड में बग द्वारा काम करने से रोका जाता है।

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

6

आप template method पैटर्न के साथ एनवीआई (गैर-वर्चुअल-इंटरफेस) नामक एक तकनीक का उपयोग कर सकते हैं।

class Printer { 
public: 
    // checks invariant, and calls the actual queuing 
    void Queue(const PrintJob&); 
private: 
    virtual void DoQueue(const PringJob&); 
}; 


void Printer::Queue(const PrintJob& job) // not virtual 
{ 
    // Validate the state in both release and debug builds. 
    // Never proceed with the queuing in a bad state. 
    if(!IsValidState()) { 
     throw std::logic_error("Printer not ready"); 
    } 

    // call virtual method DoQueue which does the job 
    DoQueue(job); 
} 

void Printer::DoQueue(const PrintJob& job) // virtual 
{ 
    // Do the actual Queuing. State is guaranteed to be valid. 
} 

क्योंकि Queue गैर आभासी है, अपरिवर्तनीय अभी भी चेक किया गया है, तो एक व्युत्पन्न वर्ग के लिए DoQueue ओवरराइड करता है: यह शायद कैसे मैं यह कर सकता है (जाहिर है, यह केवल मेरी व्यक्तिगत राय है, जो वास्तव में बहस का मुद्दा है है) है विशेष हैंडलिंग।


आपके विकल्पों में: मुझे लगता है कि यह उस स्थिति पर निर्भर करता है जिसे आप देखना चाहते हैं।

यदि यह एक आंतरिक अपरिवर्तनीय

है अगर यह एक अपरिवर्तनीय है, यह नहीं संभव अपनी कक्षा का एक उपयोगकर्ता यह उल्लंघन करने के लिए के लिए किया जाना चाहिए। कक्षा को अपने इन्टरनेट के बारे में पर ध्यान देना चाहिए। इसके लिए, मैं assert(CheckInvariant()); में ऐसा मामला होगा।

यह केवल एक विधि की एक पूर्व शर्त है

तो यह महज एक पूर्व शर्त है कि वर्ग के उपयोगकर्ता के बाद गारंटी करने के लिए होगा (जैसे कि, केवल मुद्रण प्रिंटर तैयार है), मैं ऊपर दिखाए गए अनुसार std::logic_error फेंक दूंगा।

मैं वास्तव में एक शर्त की जांच से हतोत्साहित होगा, लेकिन फिर कुछ भी नहीं कर रहा हूं।


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

+0

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

1

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

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

NonVirtual Interface लीटब से समाधान इनवेरिएंट की जांच करने का एक साफ तरीका है।

1

कठिन सवाल यह एक :)

व्यक्तिगत रूप से, मैं जब सामान को लागू करने के लिए क्या द्वारा ध्यान रखा जाना चाहिए की देखभाल करने के मैं क्या कर रहा हूँ बहुत ज्यादा में एक अपवाद के बाद से मैं आमतौर पर कर रहा हूँ फेंक जाते हैं आपका डिजाइन आम तौर पर यह वापस आता है और मुझे बाद में काटता है ...

"कुछ-लॉग-इन-एंड-डॉन-डू-डू-डू-कुछ-कुछ" के साथ मेरा व्यक्तिगत अनुभव यह है कि यह भी वापस आता है आपको काटने के लिए - विशेष रूप से यदि यह आपके मामले में लागू किया गया है (कोई वैश्विक रणनीति नहीं है, तो हर वर्ग संभावित रूप से इसे अलग-अलग तरीकों से कर सकती है)।

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

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

संबंधित मुद्दे