स्पष्ट अर्थ के साथ 2 विधियां बेहतर हैं, या सिर्फ 1 दोहरी उपयोग विधि है?


30

इंटरफ़ेस को सरल बनाने के लिए, क्या सिर्फ getBalance()विधि नहीं करना बेहतर है ? पास 0करने पर charge(float c);समान परिणाम मिलेगा:

public class Client {
    private float bal;
    float getBalance() { return bal; }
    float charge(float c) {
        bal -= c;
        return bal;
    }
}

शायद में एक नोट बनाऊं javadoc? या, बस यह जानने के लिए कक्षा उपयोगकर्ता पर छोड़ दें कि शेष राशि कैसे प्राप्त करें?


26
मैं उदारतापूर्वक यह मान रहा हूं कि उदाहरण, उदाहरण कोड है और आप मौद्रिक मूल्यों को संग्रहीत करने के लिए फ्लोटिंग पॉइंट गणित का उपयोग नहीं कर रहे हैं। अन्यथा मुझे सभी उपदेश प्राप्त करने होंगे। :-)
corsiKa

1
@ कोर्सीका नः। वह सिर्फ एक उदाहरण है। लेकिन, हाँ, मैंने फ्लोट का इस्तेमाल एक वास्तविक वर्ग में पैसे का प्रतिनिधित्व करने के लिए किया होगा ... मुझे फ़्लोटिंग पॉइंट नंबरों के बारे में याद दिलाने के लिए धन्यवाद।
david

10
कुछ भाषाएं अच्छे प्रभाव के लिए म्यूटेटर और एक्सेसर्स के बीच अंतर करती हैं। यह एक उदाहरण के रूप में केवल एक स्थिर के रूप में सुलभ है कि संतुलन पाने में असमर्थ होने के लिए भयानक कष्टप्रद होगा!
JDługosz

जवाबों:


90

आप सुझाव देते हैं कि किसी इंटरफ़ेस की जटिलता को उसके तत्वों की संख्या (तरीकों, इस मामले में) द्वारा मापा जाता है। कई लोग यह तर्क देते हैं कि यह याद रखना कि chargeविधि Clientके अतिरिक्त तत्व होने की तुलना में एक और अधिक जटिलता के संतुलन को वापस करने के लिए विधि का उपयोग किया जा सकता है getBalance। चीजों को अधिक स्पष्ट करना बहुत सरल है, खासकर उस बिंदु पर जहां यह इंटरफ़ेस में तत्वों की अधिक संख्या की परवाह किए बिना, कोई अस्पष्टता नहीं छोड़ता है।

इसके अलावा, कॉलिंग कम से कम विस्मयcharge(0) के सिद्धांत का उल्लंघन करती है , जिसे डब्ल्यूटीएफ प्रति मिनट मीट्रिक (क्लीन कोड, नीचे की छवि से) के रूप में भी जाना जाता है , जिससे टीम के नए सदस्यों (या वर्तमान लोगों के लिए, कोड से कुछ समय बाद दूर) तक कठिन हो जाता है। वे समझते हैं कि कॉल का उपयोग वास्तव में संतुलन प्राप्त करने के लिए किया जाता है। अन्य पाठकों की क्या प्रतिक्रिया होगी इस पर विचार करें:

यहां छवि विवरण दर्ज करें

इसके अलावा, chargeविधि के हस्ताक्षर एक और केवल एक ही काम करने के निर्देशों और कमांड-क्वेरी अलगाव के खिलाफ जाते हैं , क्योंकि यह ऑब्जेक्ट को एक नया मान लौटाते समय अपने राज्य को बदलने का कारण बनता है।

सब के सब, मुझे विश्वास है कि इस मामले में सबसे सरल इंटरफ़ेस होगा:

public class Client {
  private float bal;
  float getBalance() { return bal; }
  void charge(float c) { bal -= c; }
}

25
कमांड-क्वेरी अलगाव के बारे में बिंदु अत्यंत महत्वपूर्ण है, IMO। कल्पना कीजिए कि आप इस प्रोजेक्ट पर एक नए डेवलपर हैं। कोड के माध्यम से देखते हुए, यह तुरंत स्पष्ट है कि getBalance()कुछ मूल्य वापस करता है और कुछ भी संशोधित नहीं करता है। दूसरी ओर, charge(0)ऐसा लगता है कि यह शायद कुछ को संशोधित करता है ... शायद यह एक प्रॉपर्टीचेंज घटना को भेजता है? और यह क्या है, पिछला मूल्य या नया मूल्य? अब आपको इसे डॉक्स और बेकार ब्रेनपावर में देखना होगा, जिसे एक स्पष्ट विधि का उपयोग करके टाला जा सकता था।
मग Xy

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

12
CQS की सलाह जरूरी नहीं है। एक charge()विधि के लिए, यदि वह विधि समवर्ती प्रणाली में परमाणु है, तो नए या पुराने मूल्य को वापस करना उचित हो सकता है।
डायट्रिच एप

3
मुझे CQRS के लिए आपके दो उद्धरण बेहद अटपटे लगे। उदाहरण के लिए, मैं आम तौर पर मेयर के विचारों को पसंद करता हूं, लेकिन यह बताने के लिए कि क्या यह किसी चीज को उत्परिवर्तित करता है, वापसी के प्रकार को देखता है। कई समवर्ती या अपरिवर्तनीय डेटा संरचनाओं को म्यूटेशन + रिटर्न की आवश्यकता होती है। आप CQRS का उल्लंघन किए बिना एक स्ट्रिंग में कैसे शामिल होंगे? क्या आपके पास कोई बेहतर उद्धरण है?
user949300

6
लगभग कोई कोड कमांड क्वेरी सेपरेशन के अनुरूप नहीं है। यह किसी भी लोकप्रिय ऑब्जेक्ट-ओरिएंटेड भाषाओं में मुहावरेदार नहीं है, और मेरे द्वारा उपयोग की जाने वाली हर भाषा के अंतर्निहित एपीआई द्वारा उल्लंघन किया जाता है। इसे "सर्वोत्तम अभ्यास" के रूप में वर्णित करना विचित्र लगता है; क्या आप कभी भी एक सॉफ्टवेयर प्रोजेक्ट को नाम दे सकते हैं, जो वास्तव में इस पैटर्न का अनुसरण करता है?
मार्क अमेरी 19

28

IMO, आपके एप्लिकेशन के getBalance()साथ प्रतिस्थापित करना charge(0)एक सरलीकरण नहीं है। हां यह कम लाइनें हैं, लेकिन यह charge()विधि के अर्थ को बाधित करता है , जो संभावित रूप से उस रेखा के नीचे सिरदर्द पैदा कर सकता है जब आपको या किसी और को इस कोड को फिर से बनाने की आवश्यकता होती है।

यद्यपि वे एक ही परिणाम दे सकते हैं, खाते का शेष राशि शून्य के प्रभार के समान नहीं है, इसलिए यह संभवतः आपकी चिंताओं को अलग करना सबसे अच्छा होगा। उदाहरण के लिए, यदि आपको कभी charge()भी खाता लेनदेन करने के लिए लॉग इन करने के लिए संशोधित करने की आवश्यकता होती है, तो आपको अब एक समस्या है और फिर भी कार्यक्षमता को अलग करने की आवश्यकता होगी।


13

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

float charge(float c) {
    lockDownUserAccountUntilChargeClears();
    bal -= c;
    Unlock();
    return bal;
}

charge()संतुलन पाने के लिए अचानक उपयोग करने से सब कुछ अच्छा नहीं लगता।


हाँ! एक अच्छा बिंदु जो chargeकहीं अधिक भारी है।
डेडुप्लिकेटर

2

charge(0);शेष राशि प्राप्त करने के लिए उपयोग करना एक बुरा विचार है: एक दिन, कोई व्यक्ति फ़ंक्शन के अन्य उपयोग को महसूस किए बिना लगाए जा रहे आरोपों को लॉग करने के लिए वहां कुछ कोड जोड़ सकता है, और फिर हर बार किसी को शेष राशि प्राप्त होने पर इसे एक शुल्क के रूप में लॉग इन किया जाएगा। (इसके चारों ओर ऐसे तरीके हैं जैसे कि एक सशर्त बयान जो कुछ कहता है:

if (c > 0) {
    // make and log charge
}
return bal;

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

संक्षेप में: अपने उपयोगकर्ताओं या अपने प्रोग्रामर उत्तराधिकारियों पर भरोसा न करें, जो charge(0);यह महसूस करते हैं कि शेष राशि प्राप्त करने का सही तरीका है, क्योंकि जब तक कोई दस्तावेज नहीं है कि उन्हें याद न करने की गारंटी दी जाती है, तो यह स्पष्ट है कि शेष राशि प्राप्त करने का सबसे भयावह तरीका दिखता है मुमकिन।


0

मुझे पता है कि बहुत सारे उत्तर हैं, लेकिन इसके खिलाफ एक और कारण charge(0)यह है कि एक सरल टाइपो charge(9)आपके ग्राहक के संतुलन को कम कर देगा, जब भी आप अपना संतुलन प्राप्त करना चाहते हैं। यदि आपके पास अच्छी इकाई परीक्षण है, तो आप उस जोखिम को कम करने में सक्षम हो सकते हैं, लेकिन यदि आप हर कॉल पर मेहनती होने में विफल रहते हैं, तो आपको chargeयह दुर्घटना हो सकती है।


-2

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

उस मामले में, प्रत्येक कार्यान्वयन को लिखने का कार्य सरल करना, इसके उपयोग की स्पष्टता की तुलना में बहुत अधिक मूल्यवान है, क्योंकि पूर्व अनुबंध के उल्लंघन के कीड़े से बचा जाता है (दो तरीके एक-दूसरे के साथ असंगत हैं), जबकि बाद वाला केवल पठनीयता को नुकसान पहुंचाता है, जो हो सकता है एक सहायक समारोह या सुपरक्लास विधि द्वारा परिभाषित getBalanceकिया जा सकता है charge

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

यदि यह मामला नहीं है (कुछ कार्यान्वयन या बिल्कुल एक हैं) तो स्पष्टता के लिए तरीकों को अलग करना, और गैर-बीओ प्रभार के लिए प्रासंगिक व्यवहार के सरल जोड़ को अनुमति देने के लिए charge()समझ में आता है।


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

मैंने ऐसा एपीआई देखा है। लुआ 5.1+ आवंटनकर्ता इसका उपयोग करता है। आप एक फ़ंक्शन प्रदान करते हैं, और यह पारित होने वाले मापदंडों के आधार पर, आप तय करते हैं कि क्या यह नई मेमोरी आवंटित करने की कोशिश कर रहा है, पुरानी मेमोरी को डिलीट कर सकता है, या पुरानी मेमोरी से रीक्लोकेट मेमोरी। यह है दर्दनाक इस तरह के कार्यों को लिखने के लिए। मैं बहुत कुछ कहूंगा कि लुआ ने आपको सिर्फ दो कार्य दिए थे, एक आवंटन के लिए और एक सौदे के लिए।
निकोल बोलस

@ नोकोल बॉल्स: और रियललोकेशन के लिए कोई नहीं? वैसे भी, कि एक जोड़ा है "लाभ" लगभग के रूप में ही किया जा रहा है realloc, कभी-कभी, कुछ प्रणालियों पर।
डेडुप्लिकेटर

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

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