क्या यह सशर्त का उपयोग एक विरोधी पैटर्न है?


14

मैंने काम पर हमारी विरासत प्रणाली में इसे बहुत देखा है - ऐसे कार्य जो इस तरह से चलते हैं:

bool todo = false;
if(cond1)
{
  ... // lots of code here
  if(cond2)
    todo = true;
  ... // some other code here
}

if(todo)
{
  ...
}

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

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

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


3
आप इसे कैसे रिफ्लेक्टर करते हैं?
तमसे सजेलेई

13
यह मानते हुए कि टोड को कई स्थानों पर सेट किया गया है, कई गैर-तुच्छ गैर-विशिष्ट परिस्थितियों के अनुसार, मैं शायद ही कभी एक ऐसे रिफैक्टरिंग के बारे में सोच सकता हूं जो थोड़ा सा समझ में आता है। यदि कोई रिफैक्टिंग नहीं है, तो कोई एंटीपैटर्न नहीं है। टूडू चर के नामकरण को छोड़कर; "doSecurityCheck" की तरह अधिक अभिव्यंजक नाम देना चाहिए।
user281377

3
@ammoQ: +1; अगर चीजें जटिल हैं, तो वे कैसे हैं। एक ध्वज चर कुछ परिस्थितियों में बहुत अधिक समझ में आता है क्योंकि यह स्पष्ट करता है कि निर्णय लिया गया था, और आप यह खोज सकते हैं कि यह निर्णय कहां किया गया था।
डोनल फैलो

1
@ डॉनल फेलो: यदि कारण की खोज आवश्यक है, तो मैं चर को एक सूची बनाऊंगा; जब तक यह खाली है, यह "झूठा" है; जहां भी झंडा लगाया जाता है, सूची में एक कारण कोड जोड़ा जाता है। तो आप इस तरह की सूची के साथ समाप्त हो सकते हैं ["blacklisted-domain","suspicious-characters","too-long"]जो दिखाता है कि कई कारण लागू होते हैं।
user281377

2
मुझे नहीं लगता कि यह एक विरोधी पैटर्न है, लेकिन यह निश्चित रूप से एक गंध है
बाइनरी वॉरियर

जवाबों:


23

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

पहला कुछ काम करेगा और एक बूलियन मान लौटाएगा।

दूसरा जो कुछ काम करता है वह "कुछ अन्य कोड" द्वारा किया जाता है

यदि बूलियन लौटाया गया तो तीसरा सहायक कार्य करेगा।

निकाले गए तरीके शायद निजी होंगे यदि यह महत्वपूर्ण था कि दूसरा (और हमेशा) कहा जाए, यदि पहला तरीका सही लौटा।

अच्छी तरह से तरीकों का नामकरण करके, मुझे आशा है कि यह कोड को स्पष्ट कर देगा।

कुछ इस तरह:

public void originalMethod() {
    bool furtherProcessingRequired = lotsOfCode();
    someOtherCode();
    if (furtherProcessingRequired) {
        doFurtherProcessing();
    }
    return;
}

private boolean lotsOfCode() {
    if (cond1) {
        ... // lots of code here
        if(cond2) {
            return true;
        }
    }
    return false;
}

private void someOtherCode() {
    ... // some other code here
}

private void doFurtherProcessing() {
    // Do whatever is needed
}

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

बिंदु यह है कि कोड का इरादा स्पष्ट हो जाता है, जो अच्छा है ...

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


2 कार्यों में विभाजित करने के लिए अभी भी एक todoचर की आवश्यकता होगी और शायद समझना कठिन होगा।
पबबी

हाँ, मैं भी ऐसा करूँगा, लेकिन मेरा प्रश्न "टूडू" झंडे के उपयोग के बारे में अधिक था।
8

2
यदि आप समाप्त करते हैं if (check_if_needed ()) do_whatever ();, तो वहां कोई स्पष्ट झंडा नहीं है। मुझे लगता है कि यदि कोड यथोचित रूप से सरल है, तो यह कोड को बहुत अधिक और संभावित रूप से पठनीयता को नुकसान पहुंचा सकता है। आखिरकार, आप जो भी करते हैं उसका विवरण do_whateverआपके परीक्षण करने के तरीके पर प्रभाव डाल सकता है check_if_needed, ताकि सभी कोड को एक ही स्क्रीन पर एक साथ रखने के लिए उपयोगी हो। इसके अलावा, यह गारंटी नहीं देता है कि check_if_neededकोई ध्वज का उपयोग करने से बच सकता है - और यदि ऐसा होता है, तो वह शायद returnइसे करने के लिए कई बयानों का उपयोग करेगा, संभवतः सख्त एकल-निकास अधिवक्ताओं को परेशान करना।
स्टीव ३१

3
@ Pubby8 उन्होंने कहा कि "इसमें से 2 तरीके निकालें" , जिसके परिणामस्वरूप 3 तरीके हैं। वास्तविक प्रसंस्करण करने वाली 2 विधियाँ, और मूल विधि वर्कफ़्लो को समन्वित करती है। यह बहुत क्लीनर डिज़ाइन होगा।
मैटवेवी

यह ... // some other code hereप्रारंभिक वापसी मामले में छोड़ देता है
कैलथ

6

मुझे लगता है कि कुरूपता इस तथ्य के कारण है कि एक ही विधि में बहुत सारे कोड हैं, और / या चर बुरी तरह से नामांकित हैं (दोनों जिनमें से कोड को अपने दम पर बदबू आ रही है - एंटीपैटर्न अधिक अमूर्त और जटिल चीजें हैं आईएमओ)।

इसलिए यदि आप ज्यादातर कोड निचले स्तर के तरीकों में निकालते हैं, जैसा कि @Bill बताता है, बाकी सब साफ हो जाता है (कम से कम मेरे लिए)। उदाहरण के लिए

bool registrationNeeded = installSoftware(...);
if (registrationNeeded) {
  registerUser(...)
}

या हो सकता है कि आप दूसरी जाँच में फ़्लैग की जाँच को छुपाकर और किसी प्रपत्र का उपयोग करके स्थानीय ध्वज से पूरी तरह से छुटकारा पा लें

calculateTaxRefund(isTaxRefundable(...), ...)

कुल मिलाकर, मैं एक स्थानीय ध्वज चर के रूप में आवश्यक रूप से बुरा नहीं देख रहा हूं। उपरोक्त में से कौन सा विकल्प अधिक पठनीय है (= मेरे लिए बेहतर) विधि के मापदंडों की संख्या, चुने गए नामों पर निर्भर करता है, और कौन सा रूप कोड के आंतरिक तर्क के साथ अधिक सुसंगत है।


4

टूडू एक बहुत बुरा नाम है, लेकिन मुझे लगता है कि यह सब गलत हो सकता है। संदर्भ के बिना पूरी तरह से सुनिश्चित होना कठिन है।

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

bool requiresSorting = false;
if(cond1)
{
    ... // lots of code here
    if(cond2)
        requiresSorting = true;
    ... // some other code here
}

if(requiresSorting)
{
    ...
}

हालाँकि, बिल का सुझाव भी सही है। यह अभी भी अधिक पठनीय है:

bool requiresSorting = BuildList(list);
if (requiresSorting)
    SortList(list);

सिर्फ एक कदम आगे क्यों नहीं जाना चाहिए: यदि (बिल्डलिस्ट (सूची)) सॉर्टलिस्ट (सूची);
फिल एन डेबैंक

2

राज्य मशीन पैटर्न मुझे ठीक लगता है। वहाँ विरोधी पैटर्न "टूडू" (बुरा नाम) और "बहुत सारे कोड" हैं।


मुझे यकीन है कि यह सिर्फ चित्रण के लिए है, हालांकि।
लोरेन Pechtel

1
माना। मैं यह बताने की कोशिश कर रहा था कि खराब कोड में अच्छे पैटर्न को कोड की गुणवत्ता के लिए दोष नहीं दिया जाना चाहिए।
पीटीएक्स

1

यह वास्तव में निर्भर करता है। यदि आपके द्वारा संरक्षित किया गया कोड todo(मुझे आशा है कि आप उस नाम का उपयोग वास्तविक के लिए नहीं कर रहे हैं क्योंकि यह पूरी तरह से संयुक्त राष्ट्र-मैनिकोनिक है!) वैचारिक रूप से साफ-सुथरा कोड है, तो आपको एक प्रतिमान मिल गया है और C ++ के RAII या C # s जैसी किसी चीज़ का उपयोग करना चाहिए। usingइसके बजाय चीजों को संभालने के लिए निर्माण।

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


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

1

यहाँ कई जवाबों को जटिलता की जाँच से गुजरने में परेशानी होगी, कुछ ने देखा> 10।

मुझे लगता है कि यह "विरोधी पैटर्न" है जो आप देख रहे हैं। एक उपकरण खोजें जो आपके कोड के चक्रीय जटिलता को मापता है - ग्रहण के लिए प्लगइन्स हैं। यह अनिवार्य रूप से माप है कि आपका कोड परीक्षण के लिए कितना कठिन है और कोड शाखाओं की संख्या और स्तरों को शामिल करता है।

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


1

ऊपर pdr के उदाहरण का उपयोग करना, क्योंकि यह एक अच्छा उदाहरण है, मैं एक कदम आगे जाऊँगा।

उसके पास था:

bool requiresSorting = BuildList(list);
if (requiresSorting)
    SortList(list);

तो मुझे एहसास हुआ कि निम्नलिखित काम करेगा:

if(BuildList(list)) 
    SortList(list)

लेकिन उतना स्पष्ट नहीं है।

तो मूल प्रश्न के लिए, क्यों नहीं:

BuildList(list)
SortList(list)

और SortList तय अगर यह छँटाई की आवश्यकता है?

आप देखते हैं कि आपकी बिल्डलिस्ट विधि छँटाई के बारे में मालूम पड़ती है, क्योंकि यह एक ऐसे संकेत के रूप में एक बूल लौटाता है, लेकिन यह एक विधि के लिए कोई मतलब नहीं है जो सूची बनाने के लिए डिज़ाइन किया गया है।


और निश्चित रूप से अगला कदम यह पूछना है कि यह दो चरण की प्रक्रिया क्यों है। कहीं भी मुझे ऐसा कोड दिखाई देता है कि मैं BuildAndSortList (सूची)
Ian

यह कोई उत्तर नहीं है। आपने कोड का व्यवहार बदल दिया है।
D ड्रमर

ज़रुरी नहीं। फिर से, मुझे विश्वास नहीं हो रहा है कि मैं 7 साल पहले पोस्ट की गई किसी चीज़ का जवाब दे रहा हूं, लेकिन क्या नर्क :) मैं जो तर्क दे रहा था, वह यह है कि SortList में सशर्त शामिल होगा। यदि आपके पास एक इकाई परीक्षण था जो दावा करता था कि सूची केवल क्रमबद्ध की गई थी यदि शर्त x पूरी की गई थी, तो यह अभी भी पास होगा। सशर्त को SortList में ले जाकर, आप हमेशा लिखने से बचें (यदि (कुछ) तो SortList (...))
Ian

0

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

इसके अलावा अमीर डोमेन मॉडल, उस स्थिति में ऑब्जेक्ट के अंदर सिर्फ एक लाइनर बड़े काम करेगा।


0

यदि झंडा केवल एक बार सेट किया गया है तो
...
सीधे कोड के बाद ऊपर ले जाएँ
... // यहाँ कुछ अन्य कोड
फिर ध्वज को हटा दें।

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

जहाँ भी संभव हो कोड को अलग कर दें
... // बहुत सारे कोड यहाँ
दो या अधिक कार्यों में हैं, कुछ ऐसे हैं जो कुछ काम करते हैं (जो एक कमांड है) और अन्य जो या तो टूडू मूल्य (जो एक प्रश्न है) वापस करते हैं या बनाते हैं बहुत स्पष्ट है कि वे इसे संशोधित कर रहे हैं (साइड-इफेक्ट्स का उपयोग करके एक क्वेरी)

यहां चल रहे एंटी-पैटर्न में कोड ही ... मुझे संदेह है कि सामान की वास्तविक कर (कमांड्स) के साथ तर्क शाखायें आपके पैटर्न की तलाश करने वाली एंटी-पैटर्न हैं।


यह पोस्ट क्या कहती है कि मौजूदा उत्तर गायब हैं?
20

@esoterik कभी-कभी थोड़ा सीक्यूआरएस में जोड़ने का अवसर झंडे आने पर अक्सर अनदेखा हो जाता है ... झंडा बदलने का फैसला करने का तर्क एक क्वेरी का प्रतिनिधित्व करता है, जबकि काम करना एक कमांड का प्रतिनिधित्व करता है। कभी-कभी दोनों को अलग करने से कोड स्पष्ट हो सकता है। इसके अलावा, यह ऊपर दिए गए कोड में इंगित करने योग्य था कि इसे सरल किया जा सकता है क्योंकि ध्वज केवल एक शाखा में सेट किया गया है। मुझे लगता है कि झंडे एक एंटीपैटर्न नहीं हैं और अगर उनका नाम वास्तव में कोड को अधिक अभिव्यंजक बनाता है तो वे एक अच्छी बात हैं। मुझे लगता है कि जहां संभव हो, झंडे कोड में बनाए गए, सेट किए गए और उपयोग किए जाने चाहिए।
andrew pate

0

कभी-कभी मुझे लगता है कि मुझे इस पैटर्न को लागू करने की आवश्यकता है। कभी-कभी आप किसी ऑपरेशन से आगे बढ़ने से पहले कई जाँच करना चाहते हैं। दक्षता कारणों से, कुछ जाँचों को शामिल करने के लिए गणना तब तक नहीं की जाती है जब तक कि जाँच करना बिलकुल आवश्यक न हो। तो आप आमतौर पर कोड की तरह देखते हैं:

// Check individual fields for proper input

if(fieldsValidated) {
  // Perform cross-checks to see if input contains values which exist in the database

  if(valuesExist) {
    try {
      // Attempt insertion
      trx.commit();
    } catch (DatabaseException dbe) {
      trx.rollback();
      throw dbe;
    }
  } else {
    closeConnection(db);
    throwException();
  }
} else {
  closeConnection(db);
  throwException();
}

ऑपरेशन को करने की वास्तविक प्रक्रिया से मान्यता को अलग करके इसे सरल बनाया जा सकता है, इसलिए आप इसे अधिक पसंद करेंगे:

boolean proceed = true;
// Check individual fields for proper input

if(fieldsValidated) {
  // Perform cross-checks to see if input contains values which exist in the database

  if(!valuesExist) {
    proceed = false;
  }
} else {
  proceed = false;
}

// The moment of truth
if(proceed) {
  try {
    // Attempt insertion
    trx.commit();
  } catch (DatabaseException dbe) {
    trx.rollback();
    throw dbe;
  }
} else {
  if(db.isOpen()) {
    closeConnection(db);
  }
  throwException();
}

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


आपके द्वारा दिए गए उदाहरण में, मुझे लगता है कि मुझे एक फ़ंक्शन shouldIDoIt (fieldValidated, valuesExist) पसंद होगा जो उत्तर देता है। ऐसा इसलिए है क्योंकि हां / कोई निर्धारण सभी एक ही बार में नहीं किया जाता है, कोड के विपरीत जो मैं यहां काम पर देखता हूं, जहां आगे बढ़ने का निर्णय कुछ अलग-अलग गैर-संक्रामक स्पॉट में बिखरा हुआ है।
Kricket

@KelseyRider, यह ठीक बात थी। निष्पादन से मान्यता को अलग करने से आप लॉजिक को एक विधि में भर सकते हैं ताकि प्रोग्राम के समग्र लॉजिक को सरल बनाया जा सके यदि (isValidated ()) doOperation ()
नील

0

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

अब, आपके विशिष्ट उदाहरण को फिर से लिखा जा सकता है:

if(cond1)
{
    ... // lots of code here
    ... // some other code here
    if (cond2)
    {
        ...
    }
}

यह पढ़ने में आसान हो सकता है। या शायद नहीं। यह उस शेष कोड पर निर्भर करता है जिसे आपने छोड़ा था। उस कोड को अधिक सफल बनाने पर ध्यान दें।


-1

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

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

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

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