व्यवहार निर्धारित करने के लिए बूलियन पैरामीटर का उपयोग करना गलत है?


194

मैंने समय-समय पर एक अभ्यास देखा है कि "गलत" लगता है, लेकिन मैं इसके बारे में क्या गलत है, यह स्पष्ट नहीं कर सकता। या शायद यह सिर्फ मेरा पूर्वाग्रह है। यहाँ जाता हैं:

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

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


1
यह एक सवाल है कि निर्णय कहां से होते हैं। फैसलों को एक केंद्रीय जगह पर ले जाने के बजाय उन्हें सभी जगह पर ले जाएं। यह आपके पास एक अगर कोड मार्गों के एक कारक दो होने की तुलना में जटिलता को कम रखेगा।

28
मार्टिन फाउलर का इस बारे में एक लेख है: martinfowler.com/bliki/FlagArgument.html
Christoffer Hammarström


@ ChristofferHammarström अच्छा लिंक। मैं इसे अपने उत्तर में शामिल करूँगा क्योंकि यह बहुत विवरण में बताता है कि मेरे स्पष्टीकरण का एक ही विचार है।
एलेक्स

1
मैं हमेशा इस बात से सहमत नहीं हूं कि निक को क्या कहना है, लेकिन इस मामले में मैं 100% सहमत हूं: बूलियन मापदंडों का उपयोग न करें
मार्जन वेनेमा

जवाबों:


107

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

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

तो एक अत्यधिक सामंजस्यपूर्ण कार्य क्या है?

यह एक ऐसा कार्य है जो केवल एक चीज और एक चीज करता है।

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

एक्सेस कंट्रोल की चिंताओं को टास्क, एक्शन या कमांड की चिंताओं से अलग करना बेहतर होगा।

जैसा कि आप पहले ही नोट कर चुके हैं, इन चिंताओं का अंतर्कलह लगता है।

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

तो प्रश्न को शांत किया जा सकता था; यह देखते हुए कि हम सभी सहमत हैं व्यवहार चयन चयन मापदंडों से सबसे अच्छा बचा जाता है कि हम मामलों में सुधार कैसे करें?

मुझे पूरी तरह से पैरामीटर से छुटकारा मिल जाएगा। परीक्षण के लिए भी एक्सेस कंट्रोल को बंद करने की क्षमता होने से संभावित सुरक्षा जोखिम है। परीक्षण के प्रयोजनों के लिए या तो एक्सेस चेक और एक्सेस एक्सेस परिदृश्यों का परीक्षण करने के लिए या चेक करें।

Ref: सामंजस्य (कंप्यूटर विज्ञान)


रोब, क्या आप कुछ स्पष्टीकरण देंगे कि सामंजस्य क्या है और यह कैसे लागू होता है?
रे

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

1
सामंजस्य की बहुत अच्छी व्याख्या और यह अनुप्रयोग है। यह वास्तव में अधिक वोट मिलना चाहिए। और मैं सुरक्षा के मुद्दे से भी सहमत हूं ... हालांकि अगर वे सभी निजी तरीके हैं, तो यह एक छोटी संभावित भेद्यता है
रे

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


149

मैंने इस पैटर्न का उपयोग बहुत पहले, एक बहुत ही सरल कारण से बंद कर दिया था; रखरखाव का खर्च। कई बार मैंने पाया कि मेरे पास कुछ फ़ंक्शन frobnicate(something, forwards_flag)था जो मेरे कोड में कई बार कहा गया था, और कोड में उन सभी स्थानों का पता लगाने की आवश्यकता थी जहां मूल्य falseको मान के रूप में पारित किया गया था forwards_flag। आप आसानी से उन लोगों के लिए खोज नहीं कर सकते हैं, इसलिए यह एक रखरखाव सिरदर्द बन जाता है। और अगर आपको उन साइटों में से प्रत्येक पर एक बगफिक्स बनाने की आवश्यकता है, तो आप एक को मिस करने पर एक दुर्भाग्यपूर्ण समस्या हो सकती है।

लेकिन यह विशिष्ट समस्या आसानी से मौलिक रूप से दृष्टिकोण को बदलने के बिना तय की गई है:

enum FrobnicationDirection {
  FrobnicateForwards,
  FrobnicateBackwards;
};

void frobnicate(Object what, FrobnicationDirection direction);

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

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

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

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


9
वास्तव में महान ठोस उदाहरणों के साथ-साथ प्रकृति में अंतर्दृष्टि है कि हम क्या व्यवहार कर रहे हैं और यह हमें कैसे प्रभावित करता है।
रे

33
+1। मैं इसके लिए जितना संभव हो सके, एनम का उपयोग करता हूं। मैंने उन कार्यों को देखा है जहां boolबाद में अतिरिक्त पैरामीटर जोड़े गए हैं, और कॉल देखने के लिए शुरू होते हैं DoSomething( myObject, false, false, true, false )। यह पता लगाना असंभव है कि अतिरिक्त बूलियन तर्कों का क्या मतलब है, जबकि अर्थपूर्ण-नामित एनम मूल्यों के साथ, यह आसान है।
ग्रीम पेरो

17
ओह यार, अंत में फ्रोबनीटबैक के लिए एक अच्छा उदाहरण है। यह हमेशा के लिए खोज रहा है।
एलेक्स प्रिटचर्ड

38

यह जरूरी गलत नहीं है लेकिन यह एक कोड गंध का प्रतिनिधित्व कर सकता है ।

बूलियन मापदंडों के बारे में बुनियादी परिदृश्य से बचा जाना चाहिए:

public void foo(boolean flag) {
    doThis();
    if (flag)
        doThat();
}

फिर जब बुला आप आमतौर पर फोन करता हूँ foo(false)और foo(true)सही व्यवहार आप चाहते हैं पर निर्भर करता है।

यह वास्तव में एक समस्या है क्योंकि यह खराब सामंजस्य का मामला है। आप उन तरीकों के बीच एक निर्भरता बना रहे हैं जो वास्तव में आवश्यक नहीं हैं।

इस मामले में आपको क्या करना चाहिए doThisऔर doThatअलग-अलग और सार्वजनिक तरीकों के रूप में छोड़ रहा है:

doThis();
doThat();

या

doThis();

इस तरह आप फोन करने वाले को सही निर्णय छोड़ देते हैं (ठीक वैसे ही जैसे आप बूलियन पैरामीटर पास कर रहे थे) बिना कपलिंग बनाए।

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

यह मेरे द्वारा लिखे गए उदाहरणों के आधार पर इस समस्या को हल करने का एक उदाहरण है। ऐसे अन्य मामले हैं जहां एक अलग दृष्टिकोण आवश्यक होगा।

मार्टिन फाउलर का एक अच्छा लेख है, जिसमें आगे के विवरणों के बारे में बताया गया है।

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


1
"कोड गंध" शब्द को कॉल करने के लिए धन्यवाद - मुझे पता था कि यह खराब गंध है, लेकिन गंध क्या था पर काफी नहीं मिल सका। मैं जो देख रहा हूं, उसके साथ आपका उदाहरण बहुत अधिक हाजिर है।
रे

24
ऐसी बहुत सी परिस्थितियाँ हैं जहाँ if (flag) doThat()अंदर foo()वैध है। doThat()हर कॉल करने वाले व्यक्ति को पुनरावृत्ति के लिए आमंत्रित करने के बारे में निर्णय को धक्का देना, जिसे अगर आपको बाद में कुछ तरीकों के लिए पता चलता है, तो flagव्यवहार को भी कॉल करने की आवश्यकता होती है doTheOther()। मैं बहुत ही तरीकों से एक ही कक्षा में तरीकों के बीच निर्भरता रखना चाहता था क्योंकि बाद में सभी कॉल करने वालों को परिमार्जन करना पड़ता था।
ब्लरफ्ल

1
@ ब्लरफ्ल: हां, मुझे लगता है कि अधिक सीधे-सीधे रिफैक्टोरिंग या तो doOneऔर doBoth(और झूठे और सच्चे मामले के लिए, क्रमशः) और अलग तरीके का निर्माण करेंगे, जैसा कि जेम्स यंगमैन द्वारा सुझाया गया है
hugomg

@ मिस्सिंगो: आपको अभी भी कॉल करने doOne()या doBoth()निर्णय लेने के लिए कॉल करने के लिए अनावश्यक कोड को आगे बढ़ाने की समस्या होगी । सबरूटीन्स / फ़ंक्शंस / तरीके में तर्क होते हैं ताकि उनका व्यवहार विविध हो सके। सही मायने में बूलियन स्थिति के लिए एक एनम का उपयोग करना अपने आप को दोहराने की तरह लगता है अगर तर्क का नाम पहले से ही बताता है कि यह क्या करता है।
ब्लरफ्ल

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

29

सबसे पहले: प्रोग्रामिंग एक विज्ञान नहीं है, क्योंकि यह एक कला है। इसलिए कार्यक्रम के लिए बहुत कम ही "गलत" और "सही" तरीका है। अधिकांश कोडिंग-मानक केवल "प्राथमिकताएं" हैं जिन्हें कुछ प्रोग्रामर उपयोगी मानते हैं; लेकिन अंत में वे बल्कि मनमानी कर रहे हैं। इसलिए मैं कभी भी अपने आप में "गलत" होने के लिए पैरामीटर की पसंद का लेबल नहीं लगाऊंगा - और निश्चित रूप से एक सामान्य पैरामीटर के रूप में सामान्य और उपयोगी नहीं है। एक के उपयोग boolean(या एक int, उस बात के लिए) राज्य संपुटित करने के लिए कई मामलों में पूरी तरह से न्यायोचित है।

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


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

13
आपका उत्तर मुझे एक उद्धरण की याद दिलाता है, जो मुझे याद नहीं है - "यदि आपके फ़ंक्शन में 17 पैरामीटर हैं, तो आप शायद एक को याद कर रहे हैं"।
जोरिस टिम्मरमैन

मैं इस एक के साथ बहुत सहमत हूं, और यह कहने के लिए प्रश्न पर लागू होता हूं कि हाँ अक्सर इसका एक बुरा विचार एक बूलियन ध्वज में पारित करने के लिए है, लेकिन यह कभी भी उतना बुरा नहीं है जितना अच्छा है ...
JohnB

15

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


@dreza आप क्यूरिल्स लॉ की बात कर रहे हैं ।
मैटवेवी

बेशक अनुभव के साथ आपको पता होना चाहिए कि ऐसे तर्कों को कब अनदेखा करना चाहिए।
gnasher729

8

मुझे लगता है कि यहां सबसे महत्वपूर्ण बात व्यावहारिक होना है।

जब बूलियन पूरे व्यवहार को निर्धारित करता है, तो बस एक दूसरी विधि बनाएं।

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

उदाहरण के लिए:

private void FooInternal(bool flag)
{
  //do work
}

public void Foo1()
{
  FooInternal(true);
}

public void Foo2()
{
  FooInternal(false);
}

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


मैं केवल निजी तरीकों में व्यवहार को नियंत्रित करने के लिए बूलियन तर्कों का उपयोग करता हूं (जैसा कि यहां दिखाया गया है)। लेकिन समस्या यह है कि यदि FooInternalभविष्य में दृश्यता बढ़ाने के लिए कुछ डफ्स तय करते हैं, तो क्या?
ADTC

वास्तव में, मैं एक और मार्ग जाना चाहता हूँ। FooInternal के अंदर कोड को 4 टुकड़ों में विभाजित किया जाना चाहिए: बूलियन सच / झूठे मामलों को संभालने के लिए 2 कार्य, एक उस कार्य के लिए जो पहले होता है, और दूसरा उस कार्य के लिए जो उसके बाद होता है। फिर, आपका Foo1बन जाता है { doWork(); HandleTrueCase(); doMoreWork() }:। आदर्श रूप से, doWorkऔर doMoreWorkकार्य प्रत्येक (एक या एक से अधिक) असतत कार्यों (जैसे अलग-अलग कार्यों) के सार्थक भाग हैं, बंटवारे के लिए केवल दो कार्य नहीं हैं।
jpaugh

7

मुझे बिल्डर तरीकों के माध्यम से व्यवहार को अनुकूलित करने का दृष्टिकोण पसंद है जो अपरिवर्तनीय उदाहरणों को वापस करता है। यहाँ अमरूदSplitter कैसे उपयोग करता है:

private static final Splitter MY_SPLITTER = Splitter.on(',')
       .trimResults()
       .omitEmptyStrings();

MY_SPLITTER.split("one,,,,  two,three");

इस के लाभ हैं:

  • श्रेष्ठ पठनीयता
  • कॉन्फ़िगरेशन बनाम कार्रवाई विधियों का स्पष्ट पृथक्करण
  • आपको यह सोचने के लिए मजबूर करके सामंजस्य को बढ़ावा देता है कि वस्तु क्या है, उसे क्या करना चाहिए और क्या नहीं। इस मामले में यह एक है Splitter। आप कभी भी someVaguelyStringRelatedOperation(List<Entity> myEntities)कक्षा में नहीं बुलाए जाएंगे Splitter, लेकिन आप इसे StringUtilsकक्षा में एक स्थिर विधि के रूप में रखने के बारे में सोचेंगे ।
  • उदाहरण पूर्व-कॉन्फ़िगर हैं इसलिए आसानी से निर्भरता-इंजेक्शन योग्य हैं। ग्राहक को इस बारे में चिंता करने की ज़रूरत नहीं है कि सही व्यवहार प्राप्त करने के लिए पास होना चाहिए trueया falseएक विधि।

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

मुझे कॉन्फ़िगरेशन और एक्टन के तरीकों को अलग करने का विचार पसंद है!
शेर 10ck

अमरूद लाइब्रेरी के लिंक टूटे हुए हैं
जोश

4

निश्चित रूप से एक कोड गंध । यदि यह एकल उत्तरदायित्व सिद्धांत का उल्लंघन नहीं करता है, तो यह संभवतः बताओ, मत पूछो का उल्लंघन करता है । विचार करें:

यदि यह उन दो सिद्धांतों में से एक का उल्लंघन नहीं करता है, तो आपको अभी भी एक एनम का उपयोग करना चाहिए। बुलियन झंडे बुलियन जादू संख्याओं के बराबर हैं । foo(false)जितना समझ में आता है bar(42)। Enums रणनीति पैटर्न के लिए उपयोगी हो सकता है और आपको एक और रणनीति जोड़ने की सुविधा देता है। ( उन्हें उचित रूप से नाम देना याद रखें ।)

आपका विशेष उदाहरण मुझे परेशान करता है। इस झंडे को इतने तरीकों से क्यों पारित किया गया है? ऐसा लगता है जैसे आपको अपने पैरामीटर को उपवर्गों में विभाजित करने की आवश्यकता है ।


4

TL; DR: बूलियन तर्कों का उपयोग न करें।

नीचे देखें कि वे क्यों खराब हैं, और उन्हें कैसे बदलना है (बोल्ड चेहरे में)।


बूलियन तर्क को पढ़ना बहुत कठिन है, और इस तरह बनाए रखना मुश्किल है। मुख्य समस्या यह है कि उद्देश्य आमतौर पर स्पष्ट होता है जब आप विधि हस्ताक्षर पढ़ते हैं जहां तर्क का नाम दिया जाता है। हालांकि, ज्यादातर भाषाओं में एक पैरामीटर का नामकरण आमतौर पर आवश्यक नहीं होता है। तो आपके पास विरोधी पैटर्न होंगे जैसे RSACryptoServiceProvider#encrypt(Byte[], Boolean)कि बूलियन पैरामीटर यह निर्धारित करता है कि फ़ंक्शन में किस प्रकार के एन्क्रिप्शन का उपयोग किया जाना है।

तो आपको एक कॉल मिलेगा:

rsaProvider.encrypt(data, true);

जहां पाठक को यह निर्धारित करने के लिए कि trueवास्तव में नरक का क्या मतलब हो सकता है , विधि के हस्ताक्षर को देखना होगा। पूर्णांक पास करना निश्चित रूप से बुरा है:

rsaProvider.encrypt(data, 1);

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

इसे हल करने का सबसे अच्छा तरीका एक एन्यूमरेशन का उपयोग करना है । यदि आपको RSAPaddingदो मानों के साथ एक एनम पास करना है: OAEPया PKCS1_V1_5फिर आप तुरंत कोड को पढ़ सकेंगे:

rsaProvider.encrypt(data, RSAPadding.OAEP);

बूलियन में केवल दो मूल्य हो सकते हैं। इसका मतलब है कि यदि आपके पास तीसरा विकल्प है, तो आपको अपने हस्ताक्षर को फिर से भरना होगा। यदि पश्चगामी संगतता एक समस्या है, तो आमतौर पर यह आसानी से नहीं किया जा सकता है, इसलिए आपको किसी भी सार्वजनिक वर्ग को किसी अन्य सार्वजनिक विधि से विस्तारित करना होगा। यह वही है जो अंततः माइक्रोसॉफ्ट ने किया था जब उन्होंने RSACryptoServiceProvider#encrypt(Byte[], RSAEncryptionPadding)एक बूलियन के बजाय एक प्रतिरूपण (या कम से कम एक वर्ग की नकल करना) का उपयोग किया था।

पैरामीटर के रूप में पूर्ण ऑब्जेक्ट या इंटरफ़ेस का उपयोग करना और भी आसान हो सकता है , यदि पैरामीटर को खुद को पैरामीटर करने की आवश्यकता होती है। उपरोक्त उदाहरण में OAEP पैडिंग को आंतरिक रूप से उपयोग करने के लिए हैश मान के साथ परिमाणित किया जा सकता है। ध्यान दें कि अब 6 SHA-2 हैश एल्गोरिदम और 4 SHA-3 हैश एल्गोरिदम हैं, इसलिए गणना मूल्यों की संख्या में विस्फोट हो सकता है यदि आप केवल मापदंडों के बजाय एक एकल एन्यूमरेशन का उपयोग करते हैं (यह संभवतः अगली चीज है जिसे Microsoft पता लगाने जा रहा है। )।


बूलियन पैरामीटर यह भी संकेत दे सकता है कि विधि या वर्ग अच्छी तरह से डिज़ाइन नहीं किया गया है। ऊपर दिए गए उदाहरण के साथ: .NET के अलावा कोई भी क्रिप्टोग्राफिक लाइब्रेरी, विधि हस्ताक्षर में पैडिंग फ़्लैग का बिल्कुल भी उपयोग नहीं करता है।


लगभग सभी सॉफ्टवेयर गुरु जो मुझे बूलियन तर्कों के खिलाफ चेतावनी देते हैं। मिसाल के तौर पर, जोशुआ बलोच ने उनके खिलाफ बेहद सराहनीय पुस्तक "इफेक्टिव जावा" में चेतावनी दी। सामान्य तौर पर उन्हें बस इस्तेमाल नहीं किया जाना चाहिए। आप तर्क दे सकते हैं कि उनका उपयोग किया जा सकता है यदि ऐसा मामला हो जो एक पैरामीटर है जिसे समझना आसान है। लेकिन फिर भी: Bit.set(boolean)शायद दो तरीकों का उपयोग करके बेहतर तरीके से लागू किया जाता है : Bit.set()और Bit.unset()


यदि आप सीधे उस कोड को रिफलेक्टर नहीं कर सकते हैं जो आप स्थिरांक को कम से कम परिभाषित कर सकते हैं तो उन्हें अधिक पठनीय बना सकते हैं:

const boolean ENCRYPT = true;
const boolean DECRYPT = false;

...

cipher.init(key, ENCRYPT);

की तुलना में बहुत अधिक पठनीय है:

cipher.init(key, true);

भले ही आपके पास हो:

cipher.initForEncryption(key);
cipher.initForDecryption(key);

बजाय।


3

मुझे आश्चर्य है कि किसी ने नाम-मापदंडों का उल्लेख नहीं किया है ।

बूलियन-झंडे के साथ मुझे जो समस्या दिख रही है, वह यह है कि वे पठनीयता को चोट पहुँचाते हैं। उदाहरण के लिए, क्या करता trueहै

myObject.UpdateTimestamps(true);

करना? मुझे पता नहीं है। लेकिन क्या बारे में:

myObject.UpdateTimestamps(saveChanges: true);

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


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

//Enum used
double a = Math.Round(b, MidpointRounding.AwayFromZero);

//Two separate methods used
IEnumerable<Stuff> ascendingList = c.OrderBy(o => o.Key);
IEnumerable<Stuff> descendingList = c.OrderByDescending(o => o.Key); 

1
सवाल यह नहीं है कि ध्वज का निर्धारण करने वाले व्यवहार के लिए क्या बेहतर है, यह है कि क्या इस तरह के झंडे की गंध है, और यदि ऐसा है, तो क्यों
रे

2
@ रे: मुझे उन दो प्रश्नों में अंतर नहीं दिखता। ऐसी भाषा में जहां आप नामित मापदंडों के उपयोग को लागू कर सकते हैं, या जब आप यह सुनिश्चित कर सकते हैं कि नामित-पैरामीटर हमेशा उपयोग किए जाएंगे (जैसे। निजी तरीके) , बूलियन पैरामीटर ठीक हैं। यदि नामांकित पैरामीटर भाषा (C #) द्वारा लागू नहीं किया जा सकता है और वर्ग सार्वजनिक API का हिस्सा है, या यदि भाषा नामित पैरामीटर (C ++) का समर्थन नहीं करती है, तो ऐसा कोड myFunction(true)लिखा जा सकता है, यह एक कोड है- गंध।
ब्लूराजा - डैनी पफ्लुघोफ्ट

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

@ भारत मैं सहमत नहीं हूँ। यह एक सामान्य प्रोग्रामिंग समस्या है; आप आसानी से दूसरे SaveBigनाम को परिभाषित कर सकते हैं । किसी भी कोड पर शिकंजा कसा जा सकता है, इस तरह के पेंच अप विशेष रूप से नामित मापदंडों के लिए नहीं है।
मार्टेन बॉड्यूस

1
@ इंगो: अगर आप बेवकूफों से घिरे हैं, तो आप कहीं और जाकर नौकरी ढूंढते हैं। इस तरह की चीज़ आपके लिए कोड समीक्षा है।
gnasher729

1

मैं इसके बारे में क्या गलत है, इसे बहुत स्पष्ट नहीं कर सकता।

यदि यह एक कोड गंध की तरह दिखता है, एक कोड गंध की तरह लगता है और - अच्छी तरह से एक कोड गंध की तरह खुशबू आ रही है, यह शायद एक कोड गंध है।

आप क्या करना चाहते हैं:

1) साइड-इफेक्ट वाले तरीकों से बचें।

2) एक केंद्रीय, औपचारिक राज्य मशीन (जैसे के साथ आवश्यक राज्यों संभाल इस )।


1

मैं बूलियन पैरामीटर का उपयोग करने की सभी चिंताओं से सहमत हूं ताकि प्रदर्शन को निर्धारित न किया जा सके; सुधार, पठनीयता, विश्वसनीयता, जटिलता को कम करना, खराब एनकैप्सुलेशन और सामंजस्य से जोखिम कम करना और रख-रखाव के साथ स्वामित्व की कम कुल लागत।

मैंने 70 के दशक के मध्य में हार्डवेयर डिजाइन करना शुरू किया, जिसे अब हम SCADA (पर्यवेक्षी नियंत्रण और डेटा अधिग्रहण) कहते हैं और ये EPROM में मशीन कोड के साथ ठीक ट्यून किए गए हार्डवेयर थे जो मैक्रो रिमोट कंट्रोल चला रहे थे और उच्च गति डेटा एकत्र कर रहे थे।

लॉजिक को Mealey & Moore मशीन कहा जाता है जिसे हम अब Finite State Machines कहते हैं । ये भी उपरोक्त नियमों के अनुसार ही किए जाने चाहिए, जब तक कि यह परिमित निष्पादन समय के साथ वास्तविक समय की मशीन न हो और तब उद्देश्य की पूर्ति के लिए शॉर्टकट अवश्य किए जाएं।

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

लेकिन अब बहुत सारी मेमोरी और OOK की आसानी के साथ, एनकैप्सुलेशन आज एक आवश्यक घटक है लेकिन एक जुर्माना जब कोड को वास्तविक समय और SCADA मशीन कोड के लिए बाइट्स में गिना जाता है।


0

यह आवश्यक रूप से गलत नहीं है, लेकिन "उपयोगकर्ता" की कुछ विशेषता के आधार पर कार्रवाई के आपके ठोस उदाहरण में मैं एक ध्वज के बजाय उपयोगकर्ता के संदर्भ से गुजरता हूं।

यह स्पष्ट करता है और कई तरीकों से मदद करता है।

किसी को भी इस कथन को पढ़ने पर एहसास होगा कि परिणाम उपयोगकर्ता के आधार पर बदल जाएगा।

फ़ंक्शन में जिसे अंततः कहा जाता है आप आसानी से अधिक जटिल व्यावसायिक नियमों को लागू कर सकते हैं क्योंकि आप किसी भी उपयोगकर्ता विशेषताओं का उपयोग कर सकते हैं।

यदि "श्रृंखला" में एक फ़ंक्शन / विधि उपयोगकर्ता विशेषता के आधार पर कुछ अलग करती है, तो यह बहुत संभावना है कि उपयोगकर्ता विशेषताओं पर एक समान निर्भरता "श्रृंखला" में कुछ अन्य तरीकों से पेश की जाएगी।


0

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

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

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

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


एक enum शब्द, SILENT अधिक समझ में आता है, तब भी जब आप एक कक्षा / संरचना का उपयोग करते हैं (जैसा कि एक संदर्भ )। या वास्तव में बस लॉगिंग को बाहरी रूप से कॉन्फ़िगर करें - कुछ भी पारित करने की आवश्यकता नहीं है।
मार्टेन बोदवेस

-1

प्रसंग महत्वपूर्ण है। इस तरह के तरीके आईओएस में बहुत आम हैं। जैसा कि केवल एक अक्सर इस्तेमाल किया गया उदाहरण है, UINavigationController विधि प्रदान करता है -pushViewController:animated:, और animatedपैरामीटर एक BOOL है। विधि अनिवार्य रूप से एक ही फ़ंक्शन को किसी भी तरह से निष्पादित करती है, लेकिन यह एक दृश्य नियंत्रक से दूसरे में संक्रमण को एनिमेट करता है यदि आप हां में पास करते हैं, और नहीं तो आप पास नहीं करते हैं। यह पूरी तरह से उचित लगता है; यह मूर्खतापूर्ण होगा कि इस के स्थान पर दो विधियाँ प्रदान करें ताकि आप यह निर्धारित कर सकें कि एनीमेशन का उपयोग करना है या नहीं।

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

file.saveWithEncryption(false);    // is there any doubt about the meaning of 'false' here?

21
वास्तव में मुझे कोई सुराग नहीं है कि उदाहरण falseमें क्या मतलब है file.saveWithEncryption। क्या इसका मतलब है कि यह एन्क्रिप्शन के बिना बचाएगा? यदि हां, तो दुनिया में नाम में "एन्क्रिप्शन के साथ" अधिकार क्यों है? मैं समझ सकता था कि यह एक तरीका है save(boolean withEncryption), लेकिन जब मैं देखता हूं file.save(false), तो यह एक नज़र में बिल्कुल स्पष्ट नहीं है कि पैरामीटर इंगित करता है कि यह एन्क्रिप्शन के साथ या इसके बिना होगा। मुझे लगता है कि वास्तव में, यह जेम्स यंगमैन का पहला बिंदु है, एक एनम का उपयोग करने के बारे में।
रे

6
एक वैकल्पिक परिकल्पना है, जिसका falseअर्थ है, एक ही नाम की किसी भी मौजूदा फ़ाइल को ओवरविट न करें। उदाहरण से परिचित मैं जानता हूं, लेकिन यह सुनिश्चित करने के लिए कि आपको फ़ंक्शन के लिए प्रलेखन (या कोड) की जांच करनी होगी।
जेम्स यंगमैन

5
एक विधि जिसका नाम saveWithEncryptionकभी-कभी एन्क्रिप्शन से नहीं बचता है, एक बग है। यह जावेद की file.encrypt().save()तरह होना चाहिए new EncryptingOutputStream(new FileOutputStream(...)).write(file)
क्रिस्टोफर हम्मरस्ट्रॉम

2
वास्तव में, कोड जो कुछ और कहता है, वह काम नहीं करता है, और इस प्रकार एक बग है। यह नाम की एक विधि बनाने के लिए उड़ान नहीं भरता है saveWithEncryption(boolean)जो कभी-कभी एन्क्रिप्शन के बिना बचाता है, ठीक उसी तरह जैसे कि यह saveWithoutEncryption(boolean)एन्क्रिप्शन के साथ कभी-कभी बचाता है।
क्रिस्टोफर हैमरस्ट्रॉम

2
विधि खराब है, क्योंकि स्पष्ट रूप से "यहां 'झूठ' के अर्थ के बारे में संदेह है"। वैसे भी, मैं पहली बार में ऐसा तरीका नहीं लिखूंगा। सहेजना और एन्क्रिप्ट करना अलग-अलग क्रियाएं हैं, और एक विधि को एक काम करना चाहिए और इसे अच्छी तरह से करना चाहिए। इसे करने के लिए बेहतर उदाहरणों के लिए मेरी पहले की टिप्पणियाँ देखें।
क्रिस्टोफर हैमरस्टोम्म
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.