वर्ग कार्यान्वयन के अन्य प्रोग्रामरों को कैसे चेतावनी दें


96

मैं ऐसी कक्षाएं लिख रहा हूं जो "एक विशिष्ट तरीके से उपयोग की जानी चाहिए" (मुझे लगता है कि सभी वर्गों को ...) होना चाहिए।

उदाहरण के लिए, मैं fooManagerवर्ग बनाता हूं , जिसे कॉल करने की आवश्यकता होती है, कहते हैं Initialize(string,string),। और, उदाहरण को थोड़ा और आगे बढ़ाने के लिए, वर्ग बेकार हो जाएगा यदि हम इसकी ThisHappenedकार्रवाई नहीं सुनेंगे ।

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

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

मुझे लगता है कि मैं अपने वर्तमान दृष्टिकोण से परेशान हूं, जो निम्नलिखित है:

कक्षा में एक निजी बूलियन मान जोड़ें, और यदि कक्षा आरंभीकृत हो तो हर जगह जाँच करें; यदि नहीं, तो मैं यह कहते हुए एक अपवाद फेंक दूंगा कि "वर्ग को आरंभीकृत नहीं किया गया था, क्या आप सुनिश्चित हैं कि आप बुला रहे हैं .Initialize(string,string)?"।

मैं उस दृष्टिकोण के साथ ठीक हूं, लेकिन यह बहुत सारे कोड की ओर जाता है जो संकलित है और अंत में, अंतिम उपयोगकर्ता के लिए आवश्यक नहीं है।

इसके अलावा, यह कभी-कभी और भी अधिक कोड होता है जब Initiliazeकॉल करने के लिए सिर्फ एक से अधिक तरीके होते हैं। मैं अपनी कक्षाओं को बहुत अधिक सार्वजनिक तरीकों / कार्यों के साथ नहीं रखने की कोशिश करता हूं, लेकिन यह समस्या को हल नहीं कर रहा है, बस इसे उचित रखता है।

मैं यहाँ क्या देख रहा हूँ:

  • क्या मेरा दृष्टिकोण सही है?
  • क्या कोई बेहतर है?
  • आप लोग क्या करते हैं / सलाह देते हैं?
  • क्या मैं एक गैर-मुद्दे को हल करने की कोशिश कर रहा हूं? मुझे सहकर्मियों द्वारा यह बताया गया है कि यह प्रोग्रामर का उपयोग करने से पहले कक्षा की जांच करना है। मैं आदरपूर्वक असहमत हूं, लेकिन मेरा मानना ​​है कि एक और मामला है।

इसे सीधे शब्दों में कहें, मैं कोशिश कर रहा हूं कि कॉल को लागू करने के लिए कभी नहीं भूल सकता जब उस वर्ग का बाद में पुन: उपयोग किया जाता है, या किसी और द्वारा।

विनिर्देशों:

यहाँ कई प्रश्न स्पष्ट करने के लिए:

  • मैं निश्चित रूप से न केवल एक वर्ग के प्रारंभिक भाग के बारे में बात कर रहा हूं, बल्कि यह पूरे जीवनकाल में है। सहकर्मियों को दो बार एक विधि को कॉल करने से रोकें, सुनिश्चित करें कि वे वाई से पहले एक्स को कॉल करते हैं, आदि कुछ भी जो अनिवार्य और दस्तावेजीकरण में समाप्त होगा, लेकिन यह कि मैं कोड में और जितना संभव हो उतना सरल और छोटा होगा। मुझे वास्तव में Asserts का विचार पसंद आया, हालाँकि मुझे पूरा यकीन है कि मुझे कुछ अन्य विचारों को मिलाने की आवश्यकता होगी क्योंकि Asserts हमेशा संभव नहीं होगा।

  • मैं C # भाषा का उपयोग कर रहा हूँ! मैंने यह कैसे उल्लेख नहीं किया ?! मैं एक Xamarin वातावरण में हूं और एक समाधान में लगभग 6 से 9 परियोजनाओं का उपयोग करके मोबाइल एप्लिकेशन का निर्माण कर रहा हूं, जिसमें पीसीएल, आईओएस, एंड्रॉइड और विंडोज प्रोजेक्ट शामिल हैं। मैं लगभग डेढ़ साल (स्कूल और काम संयुक्त) के लिए एक डेवलपर रहा हूं, इसलिए मेरे कभी-कभी हास्यास्पद कथन \ _ प्रश्न हैं। यह सब शायद यहाँ अप्रासंगिक है, लेकिन बहुत अधिक जानकारी हमेशा एक बुरी चीज नहीं होती है।

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

मैं यह कैसे सुनिश्चित कर सकता हूं कि वह उस घटना में पंजीकृत है?

मैं यह कैसे सुनिश्चित कर सकता हूं कि वह "किसी बिंदु पर प्रक्रिया को रोकना" नहीं भूले

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

इसके अलावा, उस वर्ग में ऐसी घटनाएँ हैं जिन्हें उदाहरण के लिए "AdClicked" की तरह सुनना चाहिए। अगर सबकुछ ठीक नहीं है तो सबकुछ ठीक है, लेकिन अगर नल पंजीकृत नहीं हैं, तो हम वहां एनालिटिक्स की ट्रैकिंग खो देते हैं। विज्ञापन अभी भी काम करता है, इसलिए उपयोगकर्ता और डेवलपर को अंतर नहीं दिखेगा, और एनालिटिक्स में सिर्फ गलत डेटा होगा। इससे बचने की जरूरत है, लेकिन मुझे यकीन नहीं है कि डेवलपर कैसे जान सकते हैं कि उन्हें ताओ घटना में पंजीकृत होना चाहिए। हालांकि यह एक सरल उदाहरण है, लेकिन विचार यह है कि, "सुनिश्चित करें कि वह सार्वजनिक कार्रवाई का उपयोग करता है जो उपलब्ध है" और बिल्कुल सही समय पर!


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


5
अन्य प्रश्न का उत्तर अप्रासंगिक है, प्रश्न समान नहीं है, इसलिए वे अलग प्रश्न हैं। यदि कोई उस चर्चा को देखता है तो वह दूसरे प्रश्न का शीर्षक नहीं लिखेगा।
गिल सैंड

6
Ctor में इनिशियलाइज़ की सामग्री को मर्ज करने से क्या रोका जा सकता है ? initializeऑब्जेक्ट निर्माण के बाद देर से कॉल किया जाना चाहिए? Ctor भी "जोखिम भरा" होगा इस अर्थ में कि यह अपवादों को फेंक सकता है और निर्माण श्रृंखला को तोड़ सकता है?
स्पॉट किया गया

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

जवाबों:


161

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

लेकिन कॉल करने वालों को FooManagerइससे पहले कि वे इसे इनिशियलाइज़ कर सकते हैं, निर्माण करने की आवश्यकता है , क्योंकि यह FooManagerएक निर्भरता के रूप में चारों ओर से गुजरता है।

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

ctorArgs = ...;
getFooManager = (initInfo) -> new FooManager(ctorArgs, initInfo);
...
getFooManager(myInitInfo).fooMethod();

इसके साथ समस्या यह है कि आपको हर बार जब आप एक्सेस करते हैं, तो आपको init की जानकारी देनी होगी FooManager

यदि यह आपकी भाषा में आवश्यक है, तो आप getFooManager()ऑपरेशन को फ़ैक्टरी-जैसे या बिल्डर जैसी श्रेणी में लपेट सकते हैं ।

मैं वास्तव में रनटाइम जांच करना चाहता हूं कि initialize()विधि-प्रणाली-स्तरीय समाधान का उपयोग करने के बजाय विधि को बुलाया गया था।

समझौता करना संभव है। हम एक रैपर क्लास बनाते हैं MaybeInitializedFooManagerजिसमें एक ऐसा get()तरीका होता है जो रिटर्न करता है FooManager, लेकिन फेंकता है अगर FooManagerपूरी तरह से शुरू नहीं किया गया था। यह केवल तभी काम करता है जब आरंभीकरण आवरण के माध्यम से किया जाता है, या यदि कोई FooManager#isInitialized()विधि है।

class MaybeInitializedFooManager {
  private final FooManager fooManager;

  public MaybeInitializedFooManager(CtorArgs ctorArgs) {
    fooManager = new FooManager(ctorArgs);
  }

  public FooManager initialize(InitArgs initArgs) {
    fooManager.initialize(initArgs);
    return fooManager;
  }

  public FooManager get() {
    if (fooManager.isInitialized()) return fooManager;
    throw ...;
  }
}

मैं अपनी कक्षा का एपीआई नहीं बदलना चाहता।

उस स्थिति में, आप if (!initialized) throw;प्रत्येक विधि में शर्तों से बचना चाहेंगे । सौभाग्य से, इसे हल करने के लिए एक सरल पैटर्न है।

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

class FooManager {
  private CtorArgs ctorArgs;
  private Impl impl;

  public FooManager(CtorArgs ctorArgs) {
    this.ctorArgs = ctorArgs;
    this.impl = new UninitializedImpl();
  }

  public void initialize(InitArgs initArgs) {
    impl = new MainImpl(ctorArgs, initArgs);
  }

  public X foo() { return impl.foo(); }
  public Y bar() { return impl.bar(); }
}

interface Impl {
  X foo();
  Y bar();
}

class UninitializedImpl implements Impl {
  public X foo() { throw ...; }
  public Y bar() { throw ...; }
}

class MainImpl implements Impl {
  public MainImpl(CtorArgs c, InitArgs i);
  public X foo() { ... }
  public Y bar() { ... }
}

यह कक्षा के मुख्य व्यवहार को अंदर खींचता है MainImpl


9
मुझे पहले दो समाधान (+1) पसंद हैं, लेकिन मुझे नहीं लगता कि आपके आखिरी स्निपेट को इसके तरीकों की पुनरावृत्ति के साथ FooManagerऔर UninitialisedImplदोहराए जाने से बेहतर है if (!initialized) throw;
बर्गी

3
@ बर्गी कुछ लोग वर्गों से बहुत प्यार करते हैं। हालांकि अगर FooManagerएक है बहुत कुछ तरीकों में से, यह संभावित रूप से कुछ भूल की तुलना में आसान हो सकता है if (!initialized)जाँच करता है। लेकिन उस मामले में आपको संभवतः कक्षा को तोड़ना पसंद करना चाहिए।
इमीबिस

1
हो सकता है कि एक InInitializedFoo मुझे आरंभिक फू से बेहतर न लगे, लेकिन कुछ विकल्प / विचार देने के लिए +1।
मैथ्यू जेम्स ब्रिग्स

7
+1 कारखाना सही विकल्प है। आप इसे बदले बिना किसी डिज़ाइन में सुधार नहीं कर सकते हैं, इसलिए IMHO के उत्तर का आधा हिस्सा यह कैसे हो सकता है कि यह ओपी की मदद करने वाला नहीं है।
नाथन कूपर

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

79

ग्राहकों को किसी वस्तु का "दुरुपयोग" करने से रोकने का सबसे प्रभावी और सहायक तरीका असंभव बना देता है।

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

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

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

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

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

एक सरल उदाहरण: आपके पास एक File-object है जो आपको एक फ़ाइल से पढ़ने की अनुमति देता है। हालांकि, ग्राहक Openको ReadLineविधि को कॉल करने से पहले कॉल करने की आवश्यकता होती है, और हमेशा कॉल करने के लिए याद रखने की आवश्यकता Closeहोती है (भले ही कोई अपवाद होता है) जिसके बाद ReadLineविधि को अब और नहीं बुलाया जाना चाहिए। यह लौकिक युग्मन है। इसे एकल विधि के द्वारा टाला जा सकता है जो एक तर्क के रूप में कॉलबैक या प्रतिनिधि लेता है। विधि फ़ाइल को खोलने, कॉलबैक को कॉल करने और फिर फ़ाइल को बंद करने का प्रबंधन करती है। यह Readकॉलबैक के लिए एक विधि के साथ एक अलग इंटरफ़ेस पास कर सकता है । इस तरह, क्लाइंट के लिए सही क्रम में कॉल करने के तरीकों को भूलना संभव नहीं है।

टेम्पोरल कपलिंग के साथ इंटरफेस:

class File {
     /// Must be called on a closed file.
     /// Remember to always call Close() when you are finished
     public void Open();

     /// must be called on on open file
     public string ReadLine();

     /// must be called on an open file
     public void Close();
}

अस्थायी युग्मन के बिना:

class File {
    /// Opens the file, executes the callback, and closes the file again.
    public void Consume(Action<IOpenFile> callback);
}

interface IOpenFile {
    string ReadLine();
}

एक अधिक हैवीवेट समाधान Fileएक अमूर्त वर्ग के रूप में परिभाषित किया जाएगा जिसके लिए आपको एक (संरक्षित) पद्धति को लागू करना होगा जो खुली फाइल पर निष्पादित की जाएगी। इसे टेम्पलेट विधि पैटर्न कहा जाता है ।

abstract class File {
    /// Opens the file, executes ConsumeOpenFile(), and closes the file again.
    public void Consume();

    /// override this
    abstract protected ConsumeOpenFile();

    /// call this from your ConsumeOpenFile() implementation
    protected string ReadLine();
}

लाभ समान है: ग्राहक को एक निश्चित क्रम में कॉल करने के तरीकों को याद रखने की आवश्यकता नहीं है। गलत तरीके से तरीकों को कॉल करना संभव नहीं है।


2
इसके अलावा मुझे ऐसे मामले भी याद हैं, जहां कंस्ट्रक्टर से जाना संभव नहीं था, लेकिन मेरे पास अभी दिखाने के लिए कोई उदाहरण नहीं है
Gil Sand

2
अब उदाहरण एक कारखाने के पैटर्न का वर्णन करता है - लेकिन पहला वाक्य वास्तव में RAII प्रतिमान का वर्णन करता है । तो यह जवाब देने के लिए कौन सा है?
Ext3h

11
@ Ext3h मुझे नहीं लगता कि फैक्ट्री पैटर्न और RAII परस्पर अनन्य हैं।
पीटर बी

@PieterB नहीं, वे नहीं हैं, एक कारखाना RAII सुनिश्चित कर सकता है। लेकिन केवल अतिरिक्त निहितार्थ के साथ कि टेम्पलेट / कंस्ट्रक्टर args (कहा जाता है UnitializedFooManager) को उदाहरणों के साथ एक राज्य को साझा नहीं करना चाहिए ( InitializedFooManager), जैसा Initialize(...)कि वास्तव में Instantiate(...)शब्दार्थ है। अन्यथा, यह उदाहरण नई समस्याओं की ओर जाता है यदि एक ही टेम्प्लेट एक डेवलपर द्वारा दो बार उपयोग किया जाता है। और यह संभव नहीं है कि या तो वैधानिक रूप से रोकना / मान्य करना संभव नहीं है, यदि भाषा moveटेम्पलेट के मज़बूती से उपभोग करने के लिए शब्दार्थ का स्पष्ट रूप से समर्थन नहीं करती है।
13:

2
@ Ext3h: मैं पहले समाधान की सलाह देता हूं क्योंकि यह सबसे सरल है। लेकिन अगर ग्राहक की जरूरत है अगर आप दूसरा समाधान का उपयोग करने में सक्षम हो सकता प्रारंभ बुला, तो आप इसका इस्तेमाल नहीं कर सकते से पहले वस्तु का उपयोग करने में सक्षम हो, लेकिन।
जैक्सबी

39

IllegalStateExceptionयदि आप कोशिश करते हैं और इसका उपयोग करते हैं , तो मैं सामान्य रूप से सिर्फ इंटिआयलेशन और थ्रो (जांच) के लिए जांच करूंगा ।

हालाँकि, यदि आप संकलन-समय सुरक्षित रखना चाहते हैं (और यह प्रशंसनीय और श्रेयस्कर है), तो निर्माण और प्रारंभिक ऑब्जेक्ट को लौटाए गए कारखाने के तरीके के रूप में प्रारंभिकता का इलाज क्यों न करें।

ComponentBuilder builder = new ComponentBuilder();
Component forClients = builder.initialise(); // the 'Component' is what you give your clients

और इसलिए आप वस्तुओं के निर्माण और जीवनचक्र को नियंत्रित करते हैं, और आपके ग्राहकों Componentको आपके प्रारंभिककरण के परिणामस्वरूप मिलता है। यह प्रभावी रूप से एक आलसी तात्कालिकता है।


1
अच्छा उत्तर - एक अच्छा सक्सेसफुल उत्तर में 2 अच्छे विकल्प। वर्तमान प्रकार-सिस्टम जिम्नास्टिक के ऊपर अन्य उत्तर जो (सैद्धांतिक रूप से) मान्य हैं; लेकिन अधिकांश मामलों के लिए, ये 2 सरल विकल्प आदर्श व्यावहारिक विकल्प हैं।
थॉमस डब्ल्यू

1
नोट: .NET में, InvalidOperationException को Microsoft द्वारा प्रचारित किया जाता है जिसे आप कहते हैं IllegalStateException
12

1
मैं इस जवाब को वोट नहीं कर रहा हूं लेकिन यह वास्तव में एक अच्छा जवाब नहीं है। केवल नीले रंग से फेंकने IllegalStateExceptionया InvalidOperationExceptionबाहर करने से, एक उपयोगकर्ता कभी भी यह नहीं समझ पाएगा कि उसने क्या गलत किया। इन अपवादों को डिज़ाइन दोष को ठीक करने के लिए बाईपास नहीं बनना चाहिए।
डिस्प्लेनेम

आप ध्यान देंगे कि अपवाद फेंकना एक संभव विकल्प है और मैं अपने पसंदीदा विकल्प पर विस्तार से जाना - इस संकलन-समय को सुरक्षित बनाना। मैंने इस पर जोर देने के लिए अपना जवाब संपादित किया है
ब्रायन एग्न्यू

14

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

यदि आपके पास FooManagerहास्केल में है, तो अपने उपयोगकर्ताओं को एक ऐसा निर्माण करने की अनुमति देना आपराधिक होगा जो Foos को प्रबंधित करने में सक्षम नहीं है , क्योंकि प्रकार प्रणाली इसे इतना आसान बना देती है और वे सम्मेलन हैं जो हास्केल डेवलपर्स से उम्मीद करते हैं।

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

यदि आप पायथन लिख रहे हैं, तो आपको ऐसा करने के लिए भाषा में कोई तंत्र नहीं है।

आप शायद हास्केल, पायथन, या सी नहीं लिख रहे हैं, लेकिन वे इस बात के उदाहरण हैं कि टाइप सिस्टम कितना अपेक्षित है और कितना कम काम करता है।

अपनी भाषा में एक डेवलपर की उचित अपेक्षाओं का पालन करें , और ओवर-इंजीनियर के लिए एक ऐसे समाधान का विरोध करें, जिसमें प्राकृतिक, मुहावरेदार कार्यान्वयन न हो (जब तक कि गलती करना इतना आसान न हो और इसे पकड़ना इतना कठिन हो कि यह चरम पर जाने लायक हो लंबाई असंभव बनाने के लिए)। यदि आपके पास अपनी भाषा में इतना अनुभव नहीं है कि क्या उचित है, तो किसी ऐसे व्यक्ति की सलाह मानें, जो इसे आपसे बेहतर जानता हो।


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

3
"यह" से, मेरा मतलब संकलन-समय की त्रुटि के रूप में था, एक रनटाइम के विपरीत।
पैट्रिक कॉलिन्स

केवल पायथन 3.5 का उल्लेख करके hopping जो वर्तमान संस्करण है, एक प्लग करने योग्य प्रकार प्रणाली के माध्यम से टाइप करता है। यह निश्चित रूप से संभव है कि पायथन को कोड चलाने से पहले ही त्रुटि हो जाए क्योंकि यह आयात किया गया था। 3.5 तक यह पूरी तरह से सही था, हालांकि।
बेंजामिन ग्रुएनबाम

ओह ठीक। माना हुआ +1। उलझन में भी, यह बहुत ही व्यावहारिक था।
अल फ्लानगन

मुझे आपका स्टाइल पैट्रिक पसंद है।
गिल सैंड

4

जैसा कि आप ग्राहक को कोड चेक शिप नहीं करना चाहते हैं (लेकिन प्रोग्रामर के लिए ऐसा करना ठीक लगता है) यदि आप अपनी प्रोग्रामिंग भाषा में उपलब्ध हैं, तो आप एस्टर कार्यों का उपयोग कर सकते हैं।

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

इसलिए, इसका उपयोग करने वाला एक जावा वर्ग इस तरह दिखेगा:

/** For some reason, you have to call init and connect before the manager works. */
public class FooManager {
   private int state = 0;

   public FooManager () {
   }

   public synchronized void init() {
      assert state==0 : "you called init twice.";
      // do something
      state = 1;
   }

   public synchronized void connect() {
      assert state==1 : "you called connect before init, or connect twice.";
      // do something
      state = 2;
   }

   public void someWork() {
      assert state==2 : "You did not properly init FooManager. You need to call init and connect first.";
      // do the actual work.
   }
}

आपके कार्यक्रम के रनटाइम स्थिति की जांच करने के लिए एक महान उपकरण है जिसकी आपको आवश्यकता होती है, लेकिन वास्तव में कभी भी किसी से वास्तविक वातावरण में गलत करने की अपेक्षा नहीं करते हैं।

इसके अलावा वे काफी पतले होते हैं और अगर () फेंक ... वाक्यविन्यास के बड़े हिस्से नहीं लेते हैं, तो उन्हें पकड़े जाने की आवश्यकता नहीं है, आदि।


3

इस समस्या को देखते हुए आम तौर पर वर्तमान उत्तरों की तुलना में अधिक है, जो कि प्रारंभिककरण पर अधिक ध्यान केंद्रित करते हैं। एक ऐसी वस्तु पर विचार करें जिसके दो तरीके होंगे, a()और b()। आवश्यकता यह है कि a()हमेशा पहले कहा जाता है b()। आप एक संकलन-समय जाँच बना सकते हैं कि ऐसा होता है कि किसी नई वस्तु को वापस लाने a()और b()मूल वस्तु के बजाय नई वस्तु पर जाने से। उदाहरण कार्यान्वयन:

class SomeClass
{
   private int valueRequiredByMethodB;

   public IReadyForB a (int v) { valueRequiredByMethodB = v; return new ReadyForB(this); }

   public interface IReadyForB { public void b (); }

   private class ReadyForB : IReadyForB
   {
      SomeClass owner;
      private ReadyForB (SomeClass owner) { this.owner = owner; }
      public void b () { Console.WriteLine (owner.valueRequiredByMethodB); }
   }
}

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


2

जब मैं एक बेस क्लास लागू करता हूं जिसके लिए उपयोगी होने से पहले अतिरिक्त ऑब्जेक्ट की जानकारी या अन्य वस्तुओं के लिंक की आवश्यकता होती है, तो मैं उस बेस क्लास को अमूर्त बनाने की कोशिश करता हूं, फिर उस क्लास में कई सार विधियों को परिभाषित करता हूं जो बेस क्लास के प्रवाह में उपयोग किए जाते हैं (जैसे abstract Collection<Information> get_extra_information(args);और abstract Collection<OtherObjects> get_other_objects(args);जिसे विरासत के अनुबंध द्वारा ठोस वर्ग द्वारा लागू किया जाना चाहिए, उस आधार वर्ग के उपयोगकर्ता को आधार वर्ग द्वारा आवश्यक सभी चीजों की आपूर्ति करने के लिए मजबूर करता है।

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

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


2

उत्तर है: हाँ, नहीं, और कभी-कभी। :-)

आपके द्वारा वर्णित कुछ समस्याएं आसानी से हल हो जाती हैं, कम से कम कई मामलों में।

कम्पाइल-टाइम चेकिंग निश्चित रूप से रन-टाइम चेकिंग के लिए बेहतर है, जो कि बिना किसी चेकिंग के बेहतर है।

आरई रन-टाइम:

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

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

आरई संकलन-समय:

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

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

यदि दो कार्यों को हमेशा एक साथ चलाया जाना चाहिए, तो सरल कार्य उन्हें एक कार्य करना है। जैसे कि हर बार जब आप किसी पृष्ठ पर एक विज्ञापन जोड़ते हैं, तो आपको उसके लिए एक मैट्रिक्स हैंडलर भी जोड़ना चाहिए, फिर "ऐड विज्ञापन" फ़ंक्शन के अंदर मैट्रिक्स हैंडलर को जोड़ने के लिए कोड डालें।

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

लागू करना असंभव

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

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

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

लेकिन कुछ बिंदु भी हैं जिन पर आपको कहना है, प्रोग्रामर को फ़ंक्शन पर प्रलेखन पढ़ना चाहिए।


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


2

हां, आपकी प्रवृत्ति हाजिर है। कई साल पहले मैंने पत्रिकाओं में लेख लिखे थे (इस तरह की जगह, लेकिन मृत-पेड़ पर और हर महीने एक बार बाहर) डिबगिंग , एबगिंग और एंटिबगिंग पर चर्चा की

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

लेकिन रन-टाइम के बजाय संकलन समय पर उपयोग का पता लगाने के लिए एक परीक्षण होना वास्तव में एक ही तरह का परीक्षण है: आपको अभी भी पहले उचित कार्यों को कॉल करने और उन्हें सही तरीके से उपयोग करने के लिए जानने की आवश्यकता है।

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

आप पूछते हैं, अगर उचित उपयोग सुनिश्चित करने के लिए परीक्षण रन-टाइम पर किया जा सकता है, तो क्या आपको ऐसा करना चाहिए?

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

शायद जाँच केवल डिबग बिल्ड में की जाती है।

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

आप यह तय कर सकते हैं कि ओवरहेड बिल्कुल तुच्छ है। यदि वर्ग I / O फाइल करता है, और अतिरिक्त स्टेट बाइट और इस तरह के लिए एक परीक्षण कुछ भी नहीं है। आम iostream कक्षाएं स्ट्रीम के खराब स्थिति में होने की जाँच करती हैं।

आपकी वृत्ति अच्छी है। कीप आईटी उप!


0

सूचना छिपने (इनकैप्सुलेशन) का सिद्धांत यह है कि वर्ग के बाहर की संस्थाओं को इस वर्ग का ठीक से उपयोग करने के लिए आवश्यक से अधिक जानकारी नहीं होनी चाहिए।

आपका मामला ऐसा लगता है जैसे आप बाहर के उपयोगकर्ताओं को कक्षा के बारे में पर्याप्त बताए बिना किसी वर्ग की वस्तु प्राप्त करने की कोशिश कर रहे हैं। आपके पास जितनी जानकारी होनी चाहिए, उससे कहीं अधिक जानकारी आपने छिपाई है।

  • या तो स्पष्ट रूप से निर्माणकर्ता में उस आवश्यक जानकारी के लिए पूछें;
  • या कंस्ट्रक्टर्स को बरकरार रखें और विधियों को संशोधित करें ताकि विधियों को कॉल करने के लिए, आपको लापता डेटा प्रदान करना होगा।

ज्ञान की बातें:

* किसी भी तरह से आपको क्लास और / या कंस्ट्रक्टर और / या विधियों को फिर से डिज़ाइन करना होगा।

* सही तरीके से डिजाइन नहीं करने और सही जानकारी से क्लास को भूखा रखने से आप अपनी क्लास को एक नहीं बल्कि कई जगहों पर तोड़ सकते हैं।

* यदि आपने अपवादों और त्रुटि संदेशों को खराब तरीके से लिखा है, तो आप खुद के बारे में और भी अधिक जानकारी दे सकते हैं।

* अंत में, यदि बाहरी व्यक्ति को यह पता होना चाहिए कि उसे a, b और c को इनिशियलाइज़ करना है और फिर d (), e (), f (int) को कॉल करना है तो आपके एब्सट्रैक्शन में एक रिसाव है।


0

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

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

public abstract class Foo
{
   [TemporallyCoupled(1)]
   public abstract void Init();

   [TemporallyCoupled(2)]
   public abstract void DoBar();

   [TemporallyCoupled(3)]
   public abstract void Close();
}

1: मैंने रोसलिन कोड एनालाइज़र कभी नहीं लिखा है इसलिए यह सर्वोत्तम कार्यान्वयन नहीं हो सकता है। अपने एपीआई को सही तरीके से सत्यापित करने के लिए रोसलिन कोड एनालाइज़र का उपयोग करने का विचार 100% ध्वनि का सही उपयोग किया जा रहा है।


-1

जिस तरह से मैंने पहले इस समस्या को हल किया है वह एक निजी कंस्ट्रक्टर और MakeFooManager()क्लास के भीतर एक स्थिर विधि के साथ था। उदाहरण:

public class FooManager
{
     public string Foo { get; private set; }
     public string Bar { get; private set; }

     private FooManager(string foo, string bar)
     {
         Foo = foo;
         Bar = bar;
     }

     public static FooManager MakeFooManager(string foo, string bar)
     {
         // Can do other checks here too.
         if(foo == null || bar == null)
         {
             return null;
         }
         else
         {
             return new FooManager(foo, bar);
         }
     }
}

जब से एक कंस्ट्रक्टर लागू किया जाता है, तब तक किसी के पास कोई रास्ता नहीं होता है जिससे वह FooManagerबिना गुजरे एक उदाहरण बना सके MakeFooManager()


1
ऐसा लगता है कि केवल एक बिंदु पर बताया गया था और एक पूर्व उत्तर में समझाया गया था जो कई घंटे पहले पोस्ट किया गया था
gnat

1
क्या इसका कोई लाभ सिर्फ़ नट-चेक में है? ऐसा लगता है जैसे आपने सिर्फ एक विधि बनाई है जो एक और विधि को लपेटने के अलावा कुछ नहीं करती है। क्यों?
सारा

इसके अलावा, फू और बार सदस्य चर क्यों हैं?
पैट्रिक एम।

-1

कई दृष्टिकोण हैं:

  1. गलत का सही और कठिन उपयोग करने के लिए कक्षा को आसान बनाएं। कुछ अन्य उत्तर इस बिंदु पर विशेष रूप से ध्यान केंद्रित करते हैं, इसलिए मैं बस RAII और बिल्डर पैटर्न का उल्लेख करूंगा ।

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

  3. अपने डिबग बिल्ड में जोर का प्रयोग करें ।

  4. यदि वर्ग का उपयोग अमान्य है, तो अपवादों को फेंक दें।

* ये उन प्रकार की टिप्पणियाँ हैं जिन्हें आपको नहीं लिखना चाहिए:

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