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


39

के अनुसार यह एक बूलियन पैरामीटर का उपयोग करने के व्यवहार का निर्धारण करने के लिए गलत है? , मैं एक व्यवहार निर्धारित करने के लिए बूलियन मापदंडों का उपयोग करने से बचने के महत्व को जानता हूं, जैसे:

मूल संस्करण

public void setState(boolean flag){
    if(flag){
        a();
    }else{
        b();
    }
    c();
}

नया संस्करण:

public void setStateTrue(){
    a();
    c();
}

public void setStateFalse(){
    b();
    c();
}

लेकिन इस मामले के बारे में कैसे व्यवहार के बजाय मूल्यों को निर्धारित करने के लिए बूलियन पैरामीटर का उपयोग किया जाता है? उदाहरण के लिए:

public void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

मैं isHintOn ध्वज को खत्म करने और 2 अलग-अलग कार्यों को बनाने की कोशिश कर रहा हूं:

public void setHintOn(){
    this.layer1.visible=true;
    this.layer2.visible=false;
    this.layer3.visible=true;
}

public void setHintOff(){
    this.layer1.visible=false;
    this.layer2.visible=true;
    this.layer3.visible=false;
}

लेकिन संशोधित संस्करण कम रखरखाव योग्य लगता है क्योंकि:

  1. यह मूल संस्करण की तुलना में अधिक कोड है

  2. यह स्पष्ट रूप से नहीं दिखा सकता है कि layer2 की दृश्यता संकेत विकल्प के विपरीत है

  3. जब एक नई परत (जैसे: परत 4) जोड़ी जाती है, तो मुझे जोड़ना होगा

    this.layer4.visible=false;
    

    तथा

    this.layer4.visible=true;  
    

    setHintOn () और setHintOff () में अलग से

तो मेरा प्रश्न यह है कि यदि बूलियन पैरामीटर का उपयोग केवल मानों को निर्धारित करने के लिए किया जाता है, लेकिन व्यवहार नहीं (जैसे: उस पैरामीटर पर कोई और नहीं), तो क्या यह अभी भी उस बूलियन पैरामीटर को खत्म करने की सिफारिश की गई है?


26
यह कभी भी गलत नहीं है यदि परिणामी कोड अधिक पठनीय और बनाए रखने योग्य है ;-) मैं दो अलग तरीकों के बजाय एकल विधि का उपयोग करने की सलाह दूंगा।
हेल

32
आप एक सम्मोहक तर्क प्रस्तुत करते हैं कि इन बूलियनों को सेट करने वाली पद्धति के एक एकल कार्यान्वयन के परिणामस्वरूप कक्षा के आसान रखरखाव और इसके कार्यान्वयन की समझ होगी। बहुत अच्छा; वे वैध विचार हैं। लेकिन उन्हें समायोजित करने के लिए वर्ग के सार्वजनिक इंटरफ़ेस को विकृत करने की आवश्यकता नहीं है। अलग तरीकों सार्वजनिक इंटरफ़ेस को समझते हैं और साथ काम करने के लिए आसान हो जाएगा, तो अपने को परिभाषित setHint(boolean isHintOn)एक के रूप में निजी विधि, और जनता को जोड़ने setHintOnऔर setHintOffतरीकों कि क्रमशः फोन setHint(true)और setHint(false)
मार्क अमेरी

9
मैं उन तरीकों के नामों से बहुत दुखी हो जाऊंगा: वे वास्तव में कोई लाभ नहीं देते हैं setHint(true|false)। आलू आलू। कम से कम कुछ setHintऔर जैसे का उपयोग करें unsetHint
कोनराड रुडोल्फ


4
@kevincline यदि शर्त एक नाम है, तो आप isशुरुआत में लिखते हैं । isValidआदि तो दो शब्दों के लिए क्यों बदल? इसके अलावा, "अधिक प्राकृतिक" देखने वाले की आंखों में है। यदि आप इसे अंग्रेजी वाक्य के रूप में उच्चारण करना चाहते हैं, तो मेरे लिए "यदि इशारा है" "एक टक" के साथ "
लिस्टर

जवाबों:


95

API डिज़ाइन को इस बात पर ध्यान केंद्रित करना चाहिए कि कॉलिंग की तरफ से API के क्लाइंट के लिए सबसे अधिक उपयोग योग्य क्या है ।

उदाहरण के लिए, यदि इस नए एपीआई को कॉल करने वाले को नियमित रूप से इस तरह कोड लिखने की आवश्यकता होती है

if(flag)
    foo.setStateTrue();
else
    foo.setStateFalse();

तब यह स्पष्ट होना चाहिए कि पैरामीटर से बचना एपीआई होने से भी बदतर है जो कॉलर को लिखने की अनुमति देता है

 foo.setState(flag);

पूर्व संस्करण सिर्फ एक समस्या उत्पन्न करता है जिसे तब कॉलिंग पक्ष में हल किया जाना चाहिए (और शायद एक से अधिक बार)। इससे न तो पठनीयता बढ़ती है और न ही स्थायित्व।

कार्यान्वयन की ओर है, तथापि, यह तय नहीं करना चाहिए कि सार्वजनिक एपीआई लगता है। यदि किसी setHintपैरामीटर के साथ एक फ़ंक्शन को कार्यान्वयन में कम कोड की आवश्यकता होती है, लेकिन एक एपीआई शर्तों में setHintOn/ setHintOffक्लाइंट के लिए उपयोग करना आसान लगता है, तो कोई इसे इस तरह से लागू कर सकता है:

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

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

यह दूसरे तरीके से भी काम करता है: यदि setStateऊपर से विधि को कोड के दो अलग-अलग टुकड़ों के बीच स्विच करने की आवश्यकता होती है, तो कोड के टुकड़ों को दो अलग-अलग निजी तरीकों से फिर से बनाया जा सकता है। तो IMHO इसका मतलब यह नहीं है कि "एक पैरामीटर / एक विधि" और "शून्य पैरामीटर / दो तरीकों" के बीच आंतरिक को देखकर निर्णय लेने के लिए एक मानदंड की खोज करें। हालाँकि, जिस तरह से आप API को इसके उपभोक्ता की भूमिका में देखना चाहते हैं।

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


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

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

@ साइट: मेरा संपादन देखें।
डॉक्टर ब्राउन

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

1
@ यदि उपयोगकर्ता को किसी विशिष्ट सेटिंग की पुष्टि करने की आवश्यकता है तो Toggle()प्रदान करने के लिए सही कार्य नहीं है। वह संपूर्ण बिंदु है; यदि कॉलर केवल "इसे बदलें" की परवाह करता है और न कि "इसे समाप्त करता है" तो Toggle()वह विकल्प है जो अतिरिक्त जाँच और निर्णय लेने से बचता है। मैं उस COMMON केस को नहीं कहूंगा, और न ही मैं इसे अच्छे कारण के बिना उपलब्ध कराने की सलाह दूंगा, लेकिन यदि उपयोगकर्ता को टॉगल की आवश्यकता है, तो मैं उन्हें टॉगल दूंगा।
कामिल दकरी

40

मार्टिन फाउलर केंट बेक को अलग-अलग setOn() setOff()तरीकों की सिफारिश करते हुए उद्धृत करते हैं , लेकिन यह भी कहते हैं कि इसे अदृश्य नहीं माना जाना चाहिए:

आप [वैसा] इस तरह के एक UI नियंत्रण या डेटा स्रोत के रूप में एक बूलियन स्रोत से डेटा, खींच, तो मैं नहीं बल्कि होगा setSwitch(aValue)की तुलना में

if (aValue)
  setOn();
else
  setOff();

यह एक उदाहरण है कि कॉलर के लिए आसान बनाने के लिए एक एपीआई लिखा जाना चाहिए, इसलिए यदि हम जानते हैं कि कॉलर कहां से आ रहा है तो हमें उस जानकारी को ध्यान में रखते हुए एपीआई को डिजाइन करना चाहिए। यह भी तर्क है कि हम कभी-कभी दोनों शैलियों प्रदान कर सकते हैं यदि हमें दोनों तरीकों से कॉल करने वाले मिलते हैं।

एक और सिफारिश देने और बेहतर, संदर्भ-विशिष्ट नाम देने के लिए एक एन्यूमरेटेड वैल्यू या झंडे के प्रकार का उपयोग करना है । आपके उदाहरण में, और बेहतर हो सकता है।truefalseshowHinthideHint


16
मेरे अनुभव में, सेटस्विच (मान) लगभग हमेशा सेटऑन की तुलना में कम समग्र कोड में परिणामित होता है / ठीक है क्योंकि आपके उत्तर में दिए गए / और कोड के कारण ठीक है। मैं डेवलपर्स को शाप देता हूं जो मुझे सेटस्विच (मूल्य) के बजाय सेटऑन / सेटऑफ की एपीआई देते हैं।
17 का 26

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

@ 17of26 एक ठोस प्रतिरूप के रूप में (यह दिखाने के लिए कि "यह निर्भर करता है" इसके बजाय कि एक दूसरे के लिए बेहतर है), Apple के AppKit में एक ऐसी -[NSView setNeedsDisplay:]विधि है जहां आप पास करते हैं YESयदि कोई दृश्य redraw चाहिए और NOयदि नहीं होना चाहिए। आपको लगभग इसे कभी नहीं बताने की आवश्यकता नहीं है, इसलिए UIKit का -[UIView setNeedsDisplay]कोई पैरामीटर नहीं है। यह इसी -setDoesNotNeedDisplayविधि नहीं है ।
ग्राहम ली

2
@GrahamLee, मुझे लगता है कि फाउलर का तर्क काफी सूक्ष्म है और वास्तव में निर्णय पर टिकी हुई है। मैं bool isPremiumउनके उदाहरण में एक ध्वज का उपयोग नहीं करूंगा , लेकिन मैं BookingType bookingTypeएक विधि का उपयोग करने के लिए एक एनुम ( ) का उपयोग करूंगा , जब तक कि प्रत्येक बुकिंग के लिए तर्क काफी भिन्न न हो। "उलझा हुआ तर्क" जो कि फाउलर को संदर्भित करता है, अक्सर वांछनीय होता है यदि कोई यह देखना चाहता है कि दोनों मोड में क्या अंतर है। और अगर वे मौलिक रूप से अलग हैं, तो मैं बाहरी रूप से पैरामीटर की गई विधि को उजागर करूंगा, और आंतरिक रूप से अलग तरीकों को लागू करूंगा।
स्टीव

3

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

आइए एपीआई के साथ शुरू करते हैं, दोनों:

public void setHint(boolean isHintOn)

तथा:

public void setHintOn()
public void setHintOff()

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

setHint(true)
// Do your process
…
setHint(false)

जबकि दूसरा विकल्प आपको यह देता है:

setHintOn()
// Do your process
…
setHintOff()

जो IMO अधिक पठनीय है, इसलिए मैं इस मामले में दूसरे विकल्प के साथ जाऊंगा। जाहिर है, दोनों विकल्पों (या अधिक, आप एक एंम का उपयोग कर सकते हैं) से कुछ भी नहीं रोकता है क्योंकि ग्राहम ने कहा कि अगर उदाहरण के लिए अधिक समझ में आता है)।

मुद्दा यह है कि आपको अपने एपीआई को इस आधार पर चुनना चाहिए कि ऑब्जेक्ट क्या करना है और ग्राहक इसका उपयोग कैसे करने जा रहे हैं, न कि आप इसे कैसे लागू करने जा रहे हैं, इसके आधार पर।

फिर आपको चुनना होगा कि आप अपने सार्वजनिक एपीआई को कैसे लागू करते हैं। मान लीजिए कि हमने विधियों को setHintOnऔर setHintOffहमारे सार्वजनिक एपीआई के रूप में चुना है और वे आपके उदाहरण में इस सामान्य तर्क को साझा करते हैं। आप इस तर्क को आसानी से एक निजी विधि के माध्यम से सार कर सकते हैं (कोड डॉक से कॉपी किया गया):

private void setHint(boolean isHintOn){
    this.layer1.visible=isHintOn;
    this.layer2.visible=!isHintOn;
    this.layer3.visible=isHintOn;
}

public void setHintOn(){
   setHint(true);
}

public void setHintOff(){
   setHint(false);
}

इसके विपरीत, मान लें कि हमने setHint(boolean isHintOn)अपना API उठाया, लेकिन अपने उदाहरण को उल्टा कर दें, जो भी कारण ऑन ऑन को सेट करने के लिए इसे बंद करने के लिए पूरी तरह से अलग है। इस मामले में हम इसे इस प्रकार लागू कर सकते हैं:

public void setHint(boolean isHintOn){
    if(isHintOn){
        // Set it On
    } else {
        // Set it Off
    }    
}

या और भी:

public void setHint(boolean isHintOn){    
    if(isHintOn){
        setHintOn()
    } else {
        setHintOff()
   }    
}

private void setHintOn(){
   // Set it On
}

private void setHintOff(){
   // Set it Off 
}

मुद्दा यह है कि, दोनों ही मामलों में, हमने पहले अपना सार्वजनिक एपीआई चुना और फिर चुने हुए एपीआई (और हमारे पास जो अड़चनें हैं) को ठीक करने के लिए अपने कार्यान्वयन को अनुकूलित किया, न कि दूसरे तरीके से।

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


3
एक तरफ ध्यान दें के रूप में, अगर यह के छद्म ब्लॉक (एक छोर पर शुरुआत में बयान, एक) की तरह कुछ, तो आप शायद का उपयोग करना चाहिए beginऔर endया समानार्थी शब्दों, बस इसे स्पष्ट रूप से स्पष्ट है कि वे क्या करना है बनाने के लिए, और कहा कि एक यह सूचित करते हैं शुरुआत में एक अंत और इसके विपरीत होना चाहिए।
निक हार्टले

मैं निक के साथ सहमत हूं, और जोड़ूंगा: यदि आप सुनिश्चित करना चाहते हैं कि शुरुआत और अंत हमेशा युग्मित हों, तो आपको इसके लिए भाषा-विशिष्ट मुहावरे भी प्रदान करने चाहिए: C ++ में RAII / गुंजाइश गार्ड, usingC # में ब्लॉक, withपायथन में कथन संदर्भ प्रबंधक लैम्ब्डा या
कॉल करने

मैं दोनों बिंदुओं से सहमत हूं, मैं केवल दूसरे मामले को स्पष्ट करने के लिए एक सरल उदाहरण देने की कोशिश कर रहा था (हालांकि यह एक बुरा उदाहरण है जैसा कि आप दोनों ने बताया :))।
jesm00

2

पहली चीजें पहले: कोड स्वचालित रूप से कम रख-रखाव नहीं है, सिर्फ इसलिए कि यह थोड़ा लंबा है। स्पष्टता मायने रखती है।

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

bool isBackgroundVisible() {
    return isHintVisible;
}    

bool isContentVisible() {
    return !isHintVisible;
}

(मैंने परतों को वास्तविक नाम देने की स्वतंत्रता ली है - यदि आपके पास आपके मूल कोड में यह नहीं है, तो मैं इसके साथ शुरू करूँगा)

यह अभी भी आपको इस सवाल के साथ छोड़ देता है कि क्या कोई setHintVisibility(bool)विधि है। व्यक्तिगत रूप से, मैं इसे एक showHint()और hideHint()विधि के साथ बदलने की सिफारिश करूंगा - दोनों वास्तव में सरल होंगे और जब आप परतें जोड़ते हैं तो आपको उन्हें बदलना नहीं पड़ेगा। हालांकि, यह स्पष्ट / सही कटौती नहीं है।

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


तो tl; dr है: आप दो कार्यों में विभाजन की सलाह देते हैं क्योंकि परतों को जोड़ने पर आपको उन्हें बदलना नहीं पड़ेगा? यदि एक लेयर 4 जोड़ा जाता है, तो हमें "this.layer4.visibility = isHintOn" भी कहने की आवश्यकता हो सकती है, इसलिए मुझे यकीन नहीं है कि मैं सहमत हूं। यदि कुछ भी हो, तो इसके खिलाफ एक अवगुण है, जैसा कि अब जब एक परत को जोड़ा जाता है तो हमें दो कार्यों को संपादित करना होगा, न कि केवल एक को।
एडरिक आयरनॉर्स

नहीं, मैं (कमजोर रूप से) इसे स्पष्टता ( showHintबनाम setHintVisibility) के लिए सलाह देता हूं । मैंने नई परत का उल्लेख सिर्फ इसलिए किया क्योंकि ओपी इसके बारे में चिंतित था। इसके अलावा, हम सिर्फ एक नई विधि जोड़ने की जरूरत है: isLayer4VisibleshowHintऔर hideHintबस isHintVisibleविशेषता को सही / गलत पर सेट करें और वह बदले नहीं।
डबलयू

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

@ मैं पूरी तरह से सहमत हूं कि आप चीजों को इंजीनियर कर सकते हैं, अर्थात इसे स्पष्ट किए बिना कोड को लंबा कर सकते हैं - ओटोह, आप हमेशा स्पष्टता की कीमत पर कोड को छोटा बना सकते हैं, इसलिए यहां कोई 1: 1 संबंध नहीं है।
डबल यू

@Steve सोचो "कोड गोल्फ" - इस तरह के कोड ले रहे हैं और अधिक लाइनों में यह पुनर्लेखन करता है आमतौर पर यह स्पष्ट कर सकते हैं। Still कोड गोल्फ ”एक चरम है, लेकिन वहाँ अभी भी कई प्रोग्रामर हैं जो सोचते हैं कि सब कुछ एक चतुर अभिव्यक्ति में cramming है“ सुरुचिपूर्ण ”और शायद इससे भी तेज क्योंकि संकलक अच्छे से अनुकूलन नहीं कर रहे हैं।
ब्लैकजैक

1

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

पहला उदाहरण हालांकि समस्याग्रस्त है, क्योंकि नामकरण एक सेटर विधि को इंगित करता है, लेकिन कार्यान्वयन कुछ अलग प्रतीत होते हैं। तो आपके पास व्यवहार-स्विचिंग एंटीपैटर्न और एक भ्रामक रूप से नामित विधि है। लेकिन अगर विधि वास्तव में एक नियमित सेटर है (व्यवहार स्विचिंग के बिना), तो इसके साथ कोई समस्या नहीं है setState(boolean)। दो तरीके हैं, setStateTrue()और setStateFalse()बिना किसी लाभ के चीजों को जटिल रूप से जटिल करना है।


1

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

उदाहरण के लिए, जावा में, आप कर सकते हैं:

public enum HintState {
    SHOW_HINT(true, false, true),
    HIDE_HINT(false, true, false);

    private HintState(boolean layer1Visible, boolean layer2Visible, boolean layer3Visible) {
         // constructor body and accessors omitted for clarity
    }
}

और फिर आपका कॉलर कोड इस तरह दिखाई देगा:

setHint(HintState.SHOW_HINT);

और आपका कार्यान्वयन कोड इस तरह दिखाई देगा:

public void setHint(HintState hint) {
    this.layer1Visible = hint.isLayer1Visible();
    this.layer2Visible = hint.isLayer2Visible();
    this.layer3Visible = hint.isLayer3Visible();
}

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


0

तो मेरा प्रश्न यह है कि यदि बूलियन पैरामीटर का उपयोग केवल मानों को निर्धारित करने के लिए किया जाता है, लेकिन व्यवहार नहीं (जैसे: उस पैरामीटर पर कोई और नहीं), तो क्या यह अभी भी उस बूलियन पैरामीटर को खत्म करने की सिफारिश की गई है?

जब मुझे ऐसी चीजों के बारे में संदेह है। मुझे यह पसंद है कि स्टैक ट्रेस कैसा दिखेगा।

कई सालों तक मैंने एक PHP प्रोजेक्ट पर काम किया जिसमें एक सेटर और गेट्टर के समान फ़ंक्शन का उपयोग किया गया था । यदि आप अशक्त हो गए हैं तो यह मान लौटाएगा, अन्यथा इसे सेट करें। साथ काम करना भयानक था

यहाँ एक उदाहरण एक स्टैक ट्रेस है:

function visible() : line 440
function parent() : line 398
function mode() : line 384
function run() : line 5

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

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

function bar() : line 92
function setVisible() : line 120
function foo() : line 492
function setVisible() : line 120
function run() : line 5

अगर आप मुझसे पूछें तो यह भ्रमित करने वाला है। समान setVisibleपंक्तियों में दो अलग-अलग ट्रेस पथ मिलते हैं।

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

यहाँ कुछ युक्तियाँ हैं:

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

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


1
setVisibleस्टैक ट्रेस के बारे में मुझे क्या भ्रमित करना है कि setVisible(true)कॉल करने के लिए एक कॉल का परिणाम प्रतीत होता है setVisible(false)(या आसपास का दूसरा तरीका है, इस पर निर्भर करता है कि आपको कैसे ट्रेस हुआ है)।
डेविड के

0

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

यदि आप उस उपयोग से अधिक कुछ नहीं Enumकरते हैं जो उस स्थिति का प्रतिनिधित्व करता है जिसे आपने अपने कोड की समझ में सुधार किया है।

यह उदाहरण निम्न Nodeवर्ग का उपयोग करता है JavaFX:

public enum Visiblity
{
    SHOW, HIDE

    public boolean toggleVisibility(@Nonnull final Node node) {
        node.setVisible(!node.isVisible());
    }
}

हमेशा की तरह बेहतर होता है, जैसे कई JavaFXवस्तुओं पर पाया जाता है :

public void setVisiblity(final boolean flag);

लेकिन मुझे लगता है .setVisible()और .setHidden()उस स्थिति के लिए सबसे अच्छा समाधान है जहां झंडा एक है booleanक्योंकि यह सबसे स्पष्ट और कम से कम क्रिया है।

कई चयनों के साथ कुछ के मामले में यह इस तरह से करना और भी महत्वपूर्ण है। EnumSetसिर्फ इस कारण से मौजूद है।

एड मान के पास इस विषय पर एक बहुत अच्छा ब्लॉग पोस्ट है। मैं बस उसके बारे में क्या कहना चाहता था, इसलिए मैं इस उत्तर के लिए एक परिशिष्ट के रूप में अपने ब्लॉग पोस्ट का लिंक पोस्ट नहीं करूंगा ।


0

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

यदि सभी यूजेज निरंतर मान (उदाहरण के लिए सत्य, असत्य) पास करेंगे तो यह ओवरलोड के लिए तर्क देता है।

यदि सभी यूजेज एक वैरिएबल वैल्यू पास करते हैं तो वह पैरामीटर अप्रोच के साथ विधि के लिए तर्क देता है।

यदि उनमें से कोई भी चरम सीमा लागू नहीं होती है, तो इसका मतलब है कि क्लाइंट usages का एक मिश्रण है, इसलिए आपको यह चुनना होगा कि दोनों रूपों का समर्थन करना है, या दूसरे के अनुकूल होने के लिए ग्राहकों की एक श्रेणी बनाना है (उनके लिए क्या एक अधिक अप्राकृतिक है) शैली।


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

@ देखें, उपभोग करने वाले ग्राहक के रूप में, आप जानते हैं कि आप एक निरंतर या परिवर्तनशील मार्ग से गुजर रहे हैं या नहीं। यदि स्थिरांक गुजर रहा है, तो पैरामीटर को w / o ओवरलोड करना पसंद करें।
एरिक Eidt

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

@ पहले, अगर हम डिजाइन समय / संकलन समय पर जानते हैं कि ग्राहक सभी मामलों में एक निरंतर मूल्य (सही / गलत) का उपयोग करेंगे, तो यह बताता है कि सामान्यीकृत विधि (जो एक पैरामीटर लेता है) के बजाय वास्तव में दो अलग-अलग विशिष्ट तरीके हैं। जब इसका उपयोग नहीं किया जाता है तो मैं एक मानकीकृत विधि की व्यापकता को पेश करने के खिलाफ तर्क दूंगा - यह एक YAGNI तर्क है।
एरिक इद्दत

0

डिजाइन करने के लिए दो विचार हैं:

  • एपीआई: आप उपयोगकर्ता के लिए कौन सा इंटरफ़ेस प्रस्तुत करते हैं,
  • कार्यान्वयन: स्पष्टता, रखरखाव, आदि ...

उन्हें जब्त नहीं किया जाना चाहिए।

यह पूरी तरह से ठीक है:

  • एक एकल कार्यान्वयन के लिए एपीआई प्रतिनिधि में कई तरीके हैं,
  • एपीआई में एक ही विधि है जो शर्त के आधार पर कई कार्यान्वयन के लिए भेजती है।

जैसे, कोई भी तर्क जो किसी कार्यान्वयन डिज़ाइन की लागत / लाभों द्वारा API डिज़ाइन की लागतों / लाभों को संतुलित करने का प्रयास करता है, संदिग्ध है और इसकी सावधानीपूर्वक जांच की जानी चाहिए।


एपीआई की ओर

एक प्रोग्रामर के रूप में, मैं आमतौर पर प्रोग्रामेबल एपीआई का पक्ष लूंगा। जब मैं कॉल करने के लिए फ़ंक्शन को निर्धारित करने के लिए मूल्य पर if/ switchकथन की आवश्यकता हो, तो जब मैं किसी मूल्य को अग्रेषित कर सकता हूं, तो कोड बहुत स्पष्ट है ।

यदि प्रत्येक फ़ंक्शन अलग-अलग तर्क की अपेक्षा करता है, तो उत्तरार्द्ध आवश्यक हो सकता है।

आपके मामले में, इस प्रकार, एक एकल विधि setState(type value)बेहतर लगती है।

हालांकि , वहाँ बेनाम से भी बदतर कुछ नहीं है true, false, 2, आदि ... उन जादू मूल्यों को अपने दम पर कोई अर्थ नहीं। आदिम जुनून से बचें , और मजबूत टाइपिंग को अपनाएं।

इस प्रकार, एक एपीआई पीओवी से, मैं चाहता हूं setState(State state):।


कार्यान्वयन पक्ष पर

मैं जो आसान है उसे करने की सलाह देता हूं।

यदि विधि सरल है, तो इसे सबसे अच्छी तरह से रखा जाता है। यदि नियंत्रण प्रवाह जटिल है, तो इसे कई तरीकों से अलग करना सबसे अच्छा है, प्रत्येक एक उप-मामले या पाइपलाइन के एक चरण से निपटना।


अंत में, समूहीकरण पर विचार करें ।

आपके उदाहरण में (पठनीयता के लिए अतिरिक्त व्हाट्सएप के साथ):

this.layer1.visible = isHintOn;
this.layer2.visible = ! isHintOn;
this.layer3.visible = isHintOn;

क्यों layer2बढ़ रहा है ट्रेंड? क्या यह एक विशेषता है या यह एक बग है?

क्या 2 सूचियों का होना संभव है [layer1, layer3]और [layer2], एक स्पष्ट नाम के साथ, जिस कारण से उन्हें एक साथ समूहीकृत किया गया है, और फिर उन सूचियों पर पुनरावृत्त होने का संकेत मिलता है।

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

for (auto layer : this.mainLayers) { // layer2
    layer.visible = ! isHintOn;
}
for (auto layer : this.hintLayers) { // layer1 and layer3
    layer.visible = isHintOn;
}

कोड खुद के लिए बोलता है, यह स्पष्ट है कि दो समूह क्यों हैं और उनका व्यवहार अलग है।


0

setOn() + setOff()बनाम set(flag)प्रश्न से अलग , मैं ध्यान से विचार करूंगा कि क्या एक बूलियन प्रकार यहां सबसे अच्छा है। क्या आप सुनिश्चित हैं कि कोई तीसरा विकल्प नहीं होगा?

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

setHint(false)

बनाम

setHint(Visibility::HIDE)

एनम के साथ, किसी के द्वारा तय किए जाने के बाद यह विस्तारित करना बहुत आसान होगा कि वे 'यदि आवश्यक हो' विकल्प चाहते हैं:

enum class Visibility {
  SHOW,
  HIDE,
  IF_NEEDED // New
}

बनाम

setHint(false)
setHint(true)
setHintAutomaticMode(true) // New

0

... के अनुसार, मैं एक व्यवहार का निर्धारण करने के लिए बूलियन मापदंडों का उपयोग करने से बचने के महत्व को जानता हूं

मैं इस ज्ञान का फिर से मूल्यांकन करने का सुझाव दूंगा।

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

आपके उदाहरण में, आप अपनी विधि में पैरामीटर का सही मूल्यांकन कर रहे हैं। उस संबंध में, यह किसी भी अन्य प्रकार के पैरामीटर से बिल्कुल अलग नहीं है।

सामान्य तौर पर, बूलियन मापदंडों का उपयोग करने में कुछ भी गलत नहीं है; और जाहिर है कि कोई भी पैरामीटर एक व्यवहार निर्धारित करेगा, या आपके पास पहले स्थान पर क्यों होगा?


0

प्रश्न को परिभाषित करना

आपका शीर्षक प्रश्न "क्या यह गलत है [...]?" - लेकिन आप "गलत" के साथ क्या मतलब है ?.

C # या Java कंपाइलर के अनुसार, यह गलत नहीं है। मुझे यकीन है कि आप इसके बारे में जानते हैं और यह वह नहीं है जो आप पूछ रहे हैं। मुझे इससे डर लगता है, हमारे पास केवल nप्रोग्रामर की n+1अलग-अलग राय है। यह उत्तर प्रस्तुत करता है कि पुस्तक क्लीन कोड का इस बारे में क्या कहना है।

उत्तर

क्लीन कोड सामान्य रूप से फ़ंक्शन तर्क के खिलाफ एक मजबूत मामला बनाता है:

तर्क कठिन हैं। वे काफी वैचारिक शक्ति लेते हैं। [...] हमारे पाठकों को हर बार जब वे इसे देखते हैं, तो इसकी व्याख्या करनी होगी।

यहां एक "रीडर" एपीआई उपभोक्ता हो सकता है। यह अगला कोडर भी हो सकता है, जो नहीं जानता कि यह कोड अभी तक क्या करता है - जो आप एक महीने में हो सकते हैं। वे या तो 2 फ़ंक्शन अलग से या 1 फ़ंक्शन के माध्यम से दो बार जाएंगे , एक बार ध्यान में रखते हुए trueऔर एक बार false
संक्षेप में, जितना संभव हो उतना कम तर्कों का उपयोग करें

ध्वज वाद का विशिष्ट मामला बाद में सीधे संबोधित किया जाता है:

झंडे के तर्क बदसूरत हैं। एक समारोह में एक बूलियन पास करना वास्तव में भयानक अभ्यास है। यह तुरंत विधि के हस्ताक्षर को जटिल करता है, जोर से यह घोषणा करते हुए कि यह कार्य एक से अधिक कार्य करता है। यह एक काम करता है अगर झंडा सच है और दूसरा अगर झंडा झूठा है!

सीधे आपके सवालों के जवाब देने के लिए:
क्लीन कोड के अनुसार , उस पैरामीटर को खत्म करने की सिफारिश की जाती है।


अतिरिक्त जानकारी:

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


मैंने यहाँ बहुत सारे तर्क देखे हैं कि आपको इसे एपीआई उपयोगकर्ता पर निर्भर करना चाहिए, क्योंकि बहुत सी जगहों पर ऐसा करना बेवकूफी होगी:

if (isAfterSunset) light.TurnOn();
else light.TurnOff();

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


मुझे नहीं पता कि आप परीक्षण करते हैं - उस स्थिति में, इस पर भी विचार करें:

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


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

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

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

इतनी प्रतीक्षा, आपके उत्तर की रक्षा यह है कि शीर्षक प्रश्न का उत्तर देने के लिए बहुत अस्पष्ट है? : डी ओके ... मैंने वास्तव में सोचा था कि मैंने जिस बिंदु को उद्धृत किया है वह एक दिलचस्प ताज़ा कदम था।
वाइल्डकार्ड

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