के लिए अगर एंटीपैटर्न


9

मैं इस ब्लॉग पोस्ट पर फॉर-इफ-एंटी-पैटर्न के बारे में पढ़ रहा था , और मुझे पूरा यकीन नहीं है कि मैं समझता हूं कि यह एक एंटी-पैटर्न क्यों है।

foreach (string filename in Directory.GetFiles("."))
{
    if (filename.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase))
    {
        return new StreamReader(filename);
    }
}

प्रश्न 1:

क्या यह return new StreamReader(filename);अंदर की वजह से है for loop? या तथ्य यह है कि आपको forइस मामले में लूप की आवश्यकता नहीं है ?

जैसा कि ब्लॉग के लेखक ने बताया कि इसका कम पागल संस्करण है:

if (File.Exists("desktop.ini"))
{
    return new StreamReader("desktop.ini");
} 

दोनों एक दौड़ की स्थिति से पीड़ित हैं क्योंकि यदि फ़ाइल के निर्माण से पहले हटा दिया जाता है StreamReader, तो आपको एक मिल जाएगा File­Not­Found­Exception

प्रश्न 2:

दूसरा उदाहरण तय करने के लिए, क्या आप इसे बिना स्टेटमेंट के फिर से लिख सकते हैं, और इसके बजाय StreamReaderएक ट्राइ-कैच ब्लॉक के साथ घेर सकते हैं , और अगर यह फेंकता है File­Not­Found­Exceptionतो आप इसे catchउसी हिसाब से हैंडल करते हैं ?



1
@amon - धन्यवाद, लेकिन मैं कोई अतिरिक्त बिंदु नहीं बनाता, मैं सिर्फ यह समझने की कोशिश कर रहा हूं कि लेख क्या कह रहा है और मैं इसे कैसे सही करूंगा।

3
मैं यह ज्यादा विचार नहीं दूंगा। ब्लॉग पोस्टर कुछ मुहावरेदार कोड बना रहा है जो कोई नहीं लिखता है, इसे एक नाम देता है और इसे एक विरोधी पैटर्न कहता है। यहाँ एक और है: "मैं इसे व्यर्थ असाइनमेंट पैटर्न कहता हूं: x = x; मैं इसे हर समय किया जा रहा हूं। इसलिए बेवकूफ। मुझे लगता है कि मैं इसके बारे में एक ब्लॉग लिखूंगा।"
मार्टिन माट

4
To fix the second example, would you re-write it without the if statement, and instead surround the StreamReader with a try-catch block, and if it throws a File­Not­Found­Exception you handle it in the catch block accordingly?- हाँ, यह वही है जो मैं करूँगा। दौड़ की स्थिति को हल करना "नियंत्रण प्रवाह के रूप में अपवाद" की कुछ धारणा से अधिक महत्वपूर्ण है, और यह इसे सुरुचिपूर्ण ढंग से और सफाई से हल करता है।
रॉबर्ट हार्वे

1
यदि कोई भी फाइल दिए गए मानदंडों को पूरा नहीं करता है तो यह क्या करता है? क्या यह वापस लौटा null? आप आमतौर पर कोड को साफ करने के लिए LINQ का उपयोग कर सकते हैं जो आपके उदाहरण के कोड की तरह दिखता है: दो LINQ कॉल के बीच ऑपरेटर को return Directory.GetFiles(".").FirstOrDefault(fileName => fileName.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase))?.Select(fileName => new StreamReader(filename)); नोटिस करें ?.। इसके अलावा लोग तर्क दे सकते हैं कि इस तरह से ऑब्जेक्ट का निर्माण LINQ का सबसे उचित उपयोग नहीं है, लेकिन मुझे लगता है कि यह यहाँ ठीक है। यह आपके प्रश्न का उत्तर नहीं है, लेकिन इसके एक हिस्से पर पचा नहीं है।
पैंजरक्रिस

जवाबों:


7

यह एक प्रतिपक्षी है क्योंकि यह इसका रूप लेता है:

loop over a set of values
   if current value meets a condition
       do something with value
   end
end

और के साथ प्रतिस्थापित किया जा सकता है

do something with value

इसका एक उत्कृष्ट उदाहरण कोड है:

for (var i=0; i < 5; i++)
{
    switch (i)
        case 1:
            doSomethingWith(1);
            break;
        case 2:
            doSomethingWith(2);
            break;
        case 3:
            doSomethingWith(4);
            break;
        case 4:
            doSomethingWith(4);
            break;
    }
}

जब निम्नलिखित ठीक काम करता है:

doSomethingWith(1);
doSomethingWith(2);
doSomethingWith(3);
doSomethingWith(4);

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

इसलिए यह एक विरोधी पैटर्न है: यह "लूप एंड टेस्ट" पैटर्न लेता है और इसका दुरुपयोग करता है।

अपने दूसरे प्रश्न के बारे में: हाँ। एक "कोशिश करो" पैटर्न किसी भी स्थिति में "परीक्षण तब करें" पैटर्न से अधिक मजबूत है, जहां आपका कोड पूरे उपकरण पर एकमात्र धागा नहीं है जो परीक्षण के तहत आइटम की स्थिति को बदल सकता है।

इस कोड के साथ समस्या:

if (File.Exists("desktop.ini"))
{
    return new StreamReader("desktop.ini");
}

के बीच के समय में वह यह है कि File.Existsऔर StreamReaderकि फ़ाइल खोलने का प्रयास, एक और धागा या प्रक्रिया फ़ाइल को नष्ट कर सकता है। तो आपको एक अपवाद मिलेगा। इसलिए उस अपवाद को कुछ के माध्यम से संरक्षित करने की आवश्यकता है जैसे:

try
{
    return new StreamReader("desktop.ini");
}
catch (File­Not­Found­Exception)
{
    return null; // or whatever
}

@ फेलेटर ने एक अच्छी बात उठाई? क्या यह खुद एक एंटीपैटर्न है? क्या हम अपवादों को नियंत्रण प्रवाह के रूप में उपयोग कर रहे हैं?

यदि कोड कुछ इस तरह पढ़ें:

try
{
    if (!File.Exists("desktop.ini")
    {
        throw new IniFileMissingException();
        return new StreamReader("desktop.ini");
    }
}
catch (IniFileMissingException)
{
    return null;
}

तब मैं वास्तव में एक गौरवशाली गोटो के रूप में अपवादों का उपयोग कर रहा हूं और यह वास्तव में एक विरोधी पैटर्न होगा। लेकिन इस मामले में हम बस एक नई धारा बनाने के अवांछनीय व्यवहार के आसपास काम कर रहे हैं, इसलिए यह उस विरोधी पैटर्न का उदाहरण नहीं है। लेकिन यह उस विरोधी पैटर्न के आसपास काम करने का एक उदाहरण है।

बेशक, जो हम वास्तव में चाहते हैं वह धारा बनाने का एक और अधिक सुरुचिपूर्ण तरीका है। कुछ इस तरह:

return TryCreateStream("desktop.ini", out var stream) ? stream : null;

और मैं उस try catchकोड को एक उपयोगिता विधि में इस तरह से लपेटने की सलाह दूंगा यदि आप इस कोड का उपयोग करके खुद को पाते हैं।


1
@ फ़्लैटर, मैं इस बात से सहमत हूं कि यह आदर्श नहीं है (हालाँकि मैं विवाद करता हूं कि यह उस प्रतिमान का एक उदाहरण है जो उत्तर की बात करता है)। कोड को अपना इरादा दिखाना चाहिए, यांत्रिकी को नहीं। इसलिए मैं स्काला के तरह कुछ के पक्ष में था Try, जैसे return Try(() => new StreamReader("desktop.ini")).OrElse(null);, लेकिन सी # कि निर्माण का समर्थन नहीं करता (एक 3 पार्टी पुस्तकालय का उपयोग किए बिना), तो हम भद्दा संस्करण के साथ काम किया है।
डेविड अरनो

1
@ फ़्लैटर, मैं पूरी तरह से सहमत हूं कि अपवाद असाधारण होना चाहिए। लेकिन वे शायद ही कभी हैं। उदाहरण के लिए, एक फ़ाइल जो मौजूदा नहीं है, शायद ही असाधारण है, फिर File.Openभी अगर यह फ़ाइल नहीं मिल रही है तो एक को फेंक देगा। चूँकि हमारे पास पहले से ही एक अलौकिक अपवाद है, जिसे नियंत्रण प्रवाह के हिस्से के रूप में फँसाना एक आवश्यकता है (जब तक कि कोई व्यक्ति ऐप को दुर्घटनाग्रस्त नहीं करना चाहता)।
डेविड अरनो

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

1
@ फैलेटर: फ़ाइल संचालन के आसपास की कोशिश / पकड़ से बचने का कोई भी तरीका दौड़ की स्थिति का परिचय देता है। फ़ाइलसिस्टम जिसे आप लिखना चाहते हैं, वह फ़ाइल को कॉल करने से पहले तत्काल अनुपलब्ध हो सकता है। किसी भी जाँच को अमान्य करार देते हुए बनाएँ।
whatsisname

1
@ फ़्लैटर " आप प्रभावी रूप से यह तर्क दे रहे हैं कि हर विधि कॉल को रिटर्न प्रकार की आवश्यकता होती है ... जो प्रभावी रूप से अपवादों को अलग तरीके से फिर से स्थापित करने की कोशिश कर रहा है "। सही बात। यद्यपि वह पुनर्निवेश मेरा नहीं है। यह वर्षों से कार्यात्मक भाषाओं में उपयोग किया जाता है। वे आम तौर पर पूरे "अपवादों को नियंत्रण प्रवाह के मुद्दे के रूप में" से बचाते हैं (जो आप भ्रमित करने का दावा करते हैं कि एक सांस में एक विरोधी पैटर्न है और फिर अगले पक्ष में तर्क देते हैं) संघ प्रकारों का उपयोग करके, जैसे इस मामले में हम एक Maybe<Stream>प्रकार का उपयोग करेंगे nothingयदि फ़ाइल मौजूद नहीं है और यदि ऐसा होता है तो एक रिटर्न ।
डेविड अरनो

1

प्रश्न 1: (यह एक एंटी-लूप के लिए है)

हां, क्योंकि आपको एक क्वेरी सबसिस्टम में संग्रहित वस्तुओं के लिए अपनी खोज करने की आवश्यकता नहीं है।

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

आइए एक पल के लिए नाटक करते हैं कि आप एक डेटाबेस में एक रिकॉर्ड के बजाय एक फ़ाइल सिस्टम में एक निर्देशिका में एक रिकॉर्ड देख रहे हैं। क्या आप बल्कि देखेंगे

SELECT * FROM SomeTable;

और फिर लूप में (उदाहरण के लिए C # में) ID = 100 की तलाश में लौटे कर्सर पर, या क्वेरी करने योग्य सबसिस्टम को ऐसा करने की अनुमति देता है जो आप इसके बजाय क्या देख रहे हैं?

SELECT * FROM SomeTable WHERE ID = 100;

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


प्रश्न 2: दूसरे उदाहरण को ठीक करने के लिए, क्या आप इसे बिना स्टेटमेंट के फिर से लिख सकते हैं, और इसके बजाय स्ट्रीम-राइडर को एक ट्राइ-कैच ब्लॉक के साथ घेर लें, और यदि यह फाइलनॉटफाउंड एक्ससेप्शन फेंकता है तो आप इसे उसी के अनुसार कैच ब्लॉक में हैंडल करते हैं?

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


0

प्रश्न 1:

क्या यह नए स्ट्रीमराइडर (फ़ाइलनाम) को वापस करने के कारण है; लूप के लिए अंदर? या तथ्य यह है कि आपको इस मामले में लूप की आवश्यकता नहीं है?

स्ट्रीमरडर का इससे कोई लेना-देना नहीं है। foreachऔर के बीच मंशा के स्पष्ट संघर्ष के कारण प्रतिमान-विरोधी उभर कर आता है if:

का उद्देश्य क्या है foreach?

मुझे लगता है कि आपका उत्तर कुछ इस तरह होगा: "मैं बार-बार किसी विशेष कोड को निष्पादित करना चाहता हूं"

आप कितनी फाइलें प्रोसेस करने की उम्मीद कर रहे हैं?

चूँकि आप किसी विशेष फ़ोल्डर में केवल एक विशिष्ट फ़ाइल नाम (एक्सटेंशन सहित) रख सकते हैं, यह साबित करता है कि आपका कोड एक लागू फ़ाइल खोजने के लिए है ।

यह इस तथ्य से भी पुष्टि की जाती है कि आप तुरंत एक मूल्य लौटा रहे हैं। आप वास्तव में दूसरे मैच के बारे में परवाह नहीं करते हैं, भले ही यह मौजूद हो।


ऐसी परिस्थितियां हैं जहां यह एक विरोधी पैटर्न नहीं है।

  • यदि आप उपनिर्देशिका ( Directory.GetFiles(".", SearchOption.AllDirectories)) में भी देखते हैं , तो एक ही फ़ाइल नाम के साथ एक से अधिक फ़ाइल ढूंढना संभव है (विस्तार सहित)
  • यदि आप आंशिक फ़ाइल नाम से मेल खाते हैं (उदाहरण के लिए हर फ़ाइल जिसका नाम शुरू होता है "Test_", या हर "*.zip"फ़ाइल।

ध्यान दें कि इन दोनों मामलों के लिए आपको वास्तव में कई मैचों की प्रक्रिया करने की आवश्यकता होगी और इसलिए तुरंत मूल्य वापस नहीं करना चाहिए।


प्रश्न 2:

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

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

यही वजह है कि आप को दूर नहीं करना चाहिए if

SoftwareEngineering.SE पर इस उत्तर के अनुसार :

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

क्यों, आम तौर पर, इसके लिए एक त्वरित सारांश के रूप में, यह एक विरोधी पैटर्न है:

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

अधिक जानकारी के लिए वार्ड की विकि पर चर्चा पढ़ें ।

क्या आपको इसे एक कोशिश / पकड़ में लपेटने की आवश्यकता है, आपकी स्थिति पर अत्यधिक निर्भर है:

  • यह कैसे संभव है कि आप एक दौड़ की स्थिति का सामना करने जा रहे हैं?
  • क्या आप वास्तव में इस स्थिति को संभालने में सक्षम हैं, या क्या आप चाहते हैं कि यह समस्या उपयोगकर्ता तक पहुंचे क्योंकि आप नहीं जानते कि इसे कैसे संभालना है।

कुछ भी कभी भी "हमेशा इसका उपयोग" करने की बात नहीं है। मेरी बात साबित करने के लिए:

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

तो हम सभी इस सुरक्षा उपकरण को हर समय क्यों नहीं पहन रहे हैं?

इसका सरल उत्तर यह है क्योंकि इन्हें पहनने में कमियां हैं:

  • इसमें पैसा खर्च होता है
  • यह आपके आंदोलन को अधिक बोझिल बनाता है
  • इसे पहनना काफी गर्म हो सकता है।

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

  • निर्माण श्रमिकों को अपनी नौकरी के दौरान चोट लगने की संभावना अधिक होती है। वे एक सुरक्षा हेलमेट से लाभान्वित होते हैं।
  • दूसरी ओर, कार्यालय के कर्मचारियों के घायल होने की बहुत कम संभावना है। सुरक्षा हेलमेट इसके लायक नहीं हैं।
  • एक कार्यालय कर्मचारी की तुलना में एक स्वाट टीम के सदस्य को गोली मारने की अधिक संभावना है।

क्या आपको कॉल को एक कोशिश / पकड़ में लपेटना चाहिए? यह बहुत कुछ इस बात पर निर्भर करता है कि क्या ऐसा करने के फायदे इसे लागू करने की लागत को बढ़ाते हैं।

ध्यान दें कि दूसरों का तर्क हो सकता है कि इसे लपेटने के लिए केवल कुछ कीस्ट्रोक्स लगते हैं, इसलिए यह स्पष्ट रूप से किया जाना चाहिए। लेकिन यह पूरा तर्क नहीं है:

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

तो चुनाव आपका है। क्या ऐसा करने का कोई लाभ है? क्या आपको लगता है कि यह एप्लिकेशन को बेहतर बनाता है, इसे लागू करने के प्रयास से कहीं अधिक?

अद्यतन - एक टिप्पणी से मैंने दूसरे उत्तर पर लिखा, क्योंकि मुझे लगता है कि यह आपके लिए भी एक प्रासंगिक विचार है:

यह आसपास के संदर्भ पर बहुत टिका है।

  • यदि स्ट्रीमरडर के उद्घाटन से पहले है if(!File.Exists) File.Create(), तो स्ट्रीमरडर को खोलने पर फ़ाइल की अनुपस्थिति वास्तव में असाधारण है
  • यदि फ़ाइल नाम मौजूदा फ़ाइलों की सूची से चुना गया था, तो इसकी अचानक अनुपस्थिति फिर से असाधारण है
  • यदि आप एक स्ट्रिंग के साथ काम कर रहे हैं जिसे आपने वास्तव में निर्देशिका के खिलाफ परीक्षण नहीं किया है; तब फ़ाइल की अनुपस्थिति एक पूरी तरह से तार्किक परिणाम है, और इसलिए असाधारण नहीं है
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.