क्या किसी वर्ग के लिए अपनी सार्वजनिक पद्धति का उपयोग करना ठीक है?


23

पृष्ठभूमि

वर्तमान में मेरे पास एक ऐसी स्थिति है जहां मेरे पास एक वस्तु है जो एक डिवाइस द्वारा प्रेषित और प्राप्त दोनों है। इस संदेश के कई निर्माण हैं, निम्नानुसार हैं:

public void ReverseData()
public void ScheduleTransmission()

ScheduleTransmissionविधि की जरूरत है कॉल करने के लिए ReverseDataविधि जब भी यह कहा जाता है। हालांकि, ऐसे समय होते हैं जहां मुझे ReverseDataबाहरी रूप से कॉल करने की आवश्यकता होगी (और मुझे पूरी तरह से नामस्थान के बाहर जोड़ना चाहिए ) जहां से ऑब्जेक्ट को एप्लिकेशन में त्वरित किया जाता है।

"प्राप्त" के रूप में मेरा मतलब है कि डेटा को अन-रिवर्स करने के लिए ReverseDataएक object_receivedइवेंट-हैंडलर में बाहरी रूप से कहा जाएगा ।

सवाल

क्या यह आम तौर पर किसी वस्तु को अपने स्वयं के सार्वजनिक तरीकों को कॉल करने के लिए स्वीकार्य है?


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

@Kan ये मेरे तरीकों के असली नाम नहीं हैं, लेकिन बारीकी से संबंधित हैं। वास्तव में "रिवर्स डेटा" केवल कुल शब्द के 8 बिट्स को उलट देता है और यह तब होता है जब हम प्राप्त करते हैं और प्रसारित करते हैं।
स्नूप

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

2
@StevieV मेरा मानना ​​है कि यह केवल चिंता का कारण है। ऐसा लगता है कि आप एक ऐसी विधि का खुलासा कर रहे हैं जो वस्तु की स्थिति को बदल देता है, और राज्य मुख्य रूप से इस बात पर निर्भर करता है कि विधि को कितनी बार कहा जाता है। यह आपके कोड में ऑब्जेक्ट की स्थिति का ट्रैक रखने की कोशिश में एक दुःस्वप्न के लिए बनाता है। यह मुझे और अधिक लगता है जैसे आप इन वैचारिक राज्यों से अलग-अलग डेटा प्रकारों से लाभान्वित होंगे , इसलिए आप यह बता सकते हैं कि किसी भी दिए गए कोड में यह क्या है, इसके बारे में चिंता किए बिना।
jpmc26

2
@StevieV डेटा और सीरियललाइडडैट जैसा कुछ। डेटा वह है जो कोड देखता है और SerializedData वह है जो नेटवर्क के माध्यम से भेजा जाता है। फिर एक प्रकार से दूसरे प्रकार में रूपांतरित डेटा (या बल्कि क्रमबद्ध / deserialize डेटा) बनाते हैं।
csiz

जवाबों:


33

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

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


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

2
"ताकि किसी को आश्चर्य न हो जब ReverseData () को ओवरराइड करने से शिड्यूलट्रांसमीशन () के काम करने का तरीका बदल जाता है" : नहीं, वास्तव में नहीं। कम से कम C # में नहीं (ध्यान दें कि प्रश्न में C # टैग है)। जब तक ReverseData()अमूर्त नहीं है, चाहे आप बच्चे की कक्षा में कुछ भी करें, यह हमेशा मूल पद्धति है जिसे कहा जाएगा।
आर्सेनी मूरज़ेंको

2
@ मेनमा या virtual... और यदि आप विधि को ओवरराइड कर रहे हैं, तो परिभाषा के अनुसार यह होना चाहिए virtualया abstract
पोकेचू 22

1
@ Pokechu22: वास्तव में, virtualभी काम करता है। सभी मामलों में, ओपी के अनुसार, हस्ताक्षर है public void ReverseData(), इसलिए सामान को ओवरराइड करने के बारे में जवाब का हिस्सा थोड़ा भ्रामक है।
Arseni Mourzenko

@ मेनमा आप कह रहे हैं कि अगर एक आधार वर्ग विधि एक आभासी विधि कहती है तो यह सामान्य आभासी तरीके से व्यवहार नहीं करती है जब तक कि यह नहीं थी abstract? मैंने abstractबनाम पर जवाब देखा virtualऔर उस उल्लेख को नहीं देखा: बल्कि सार का मतलब है कि निर्दोष आधार का कोई संस्करण नहीं है।
JDługosz

20

पूर्ण रूप से।

एक विधि की दृश्यता का एकमात्र उद्देश्य कक्षा के बाहर या बाल वर्ग के भीतर किसी विधि तक पहुँच से इनकार करना है; सार्वजनिक, संरक्षित और निजी तरीकों को हमेशा कक्षा के अंदर ही बुलाया जा सकता है।

सार्वजनिक तरीकों को कॉल करने में कुछ भी गलत नहीं है। आपके प्रश्न में दृष्टांत एक ऐसी स्थिति का सही उदाहरण है, जहाँ दो सार्वजनिक विधियाँ ठीक वही हैं जो आपको करनी चाहिए।

हालाँकि, आप निम्न पैटर्न को ध्यान से देख सकते हैं:

  1. एक विधि इस तरह से Hello()कहता World(string, int, int)है:

    Hello()
    {
        this.World("Some magic value here", 0, 100);
    }

    इस पैटर्न से बचें। इसके बजाय, वैकल्पिक तर्कों का उपयोग करें । वैकल्पिक तर्क खोज को आसान बनाते हैं: कॉल करने वाले Hello(को यह आवश्यक नहीं पता होगा कि एक विधि है, जो डिफ़ॉल्ट मानों के साथ विधि को कॉल करना संभव बनाती है। वैकल्पिक तर्क भी स्व-दस्तावेजीकरण हैं। World()कॉलर को यह नहीं दिखाता कि वास्तविक डिफ़ॉल्ट मान क्या हैं।

  2. एक विधि इस तरह से Hello(ComplexEntity)कहता World(string, int, int)है:

    Hello(ComplexEntity entity)
    {
        this.World(entity.Name, entity.Start, entity.Finish);
    }

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

  3. एक विधि बस किसी भी पर्याप्त मूल्य को जोड़ने के बिना अन्य सार्वजनिक तरीकों को बुलाती है।

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

  4. एक विधि इनपुट को मान्य करती है और फिर दूसरी विधि को कॉल करती है:

    Hello(string name, int start, int end)
    {
        if (name == null) throw new ArgumentNullException(...);
        if (start < 0) throw new OutOfRangeException(...);
        ...
        if (end < start) throw new ArgumentException(...);
    
        this.World(name, start, end);
    }

    इसके बजाय, Worldअपने मापदंडों को स्वयं मान्य करना चाहिए, या निजी होना चाहिए।


क्या वही विचार लागू होंगे यदि ReverseData वास्तव में आंतरिक था, और "ऑब्जेक्ट" के उदाहरण वाले नामस्थान में उपयोग किया गया था?
स्नूप

1
@StevieV: हाँ, बिल्कुल।
आर्सेनी मूरज़ेंको

@ मेनमा मुझे समझ नहीं आ रहा है कि अंक 1 और 2 के
कपोल

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

यहां तक ​​कि 1 मामले में, मैं आमतौर पर वैकल्पिक तर्कों के बजाय अधिभार पसंद करता हूं। यदि विकल्प ऐसे हैं कि ओवरलोड का उपयोग करना एक विकल्प नहीं है या विशेष रूप से बड़ी संख्या में ओवरलोड का उत्पादन करता है, तो मैं एक कस्टम प्रकार बनाऊंगा और तर्क के रूप में उपयोग करूंगा (जैसा कि सुझाव 2 में है), या कोड को रीफ़्रैक्टर करें।
ब्रायन

8

यदि कुछ सार्वजनिक है, तो उसे किसी भी समय किसी भी सिस्टम द्वारा कॉल किया जा सकता है। कोई कारण नहीं है कि आप उन प्रणालियों में से एक भी नहीं हो सकते हैं!

अत्यधिक अनुकूलित पुस्तकालयों में, आप मुहावरे की नकल करना चाहते हैं java.util.ArrayList#ensureCapacity(int)

ensureCapacity

  • ensureCapacity सार्वजनिक है
  • ensureCapacity सभी आवश्यक सीमा की जाँच और डिफ़ॉल्ट मान इत्यादि हैं।
  • ensureCapacity कॉल ensureExplicitCapacity

ensureCapacityInternal

  • ensureCapacityInternal निजी है
  • ensureCapacityInternal न्यूनतम त्रुटि जाँच है क्योंकि सभी इनपुट कक्षा के अंदर से आते हैं
  • ensureCapacityInternal ALSO कॉल ensureExplicitCapacity

ensureExplicitCapacity

  • ensureExplicitCapacity ALSO निजी है
  • ensureExplicitCapacityहै कोई त्रुटि जाँच
  • ensureExplicitCapacity वास्तविक काम करता है
  • ensureExplicitCapacityके अलावा ensureCapacityऔर कहीं से नहीं बुलाया जाता हैensureCapacityInternal

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

हालाँकि, इसका उपयोग JDK में सबसे अधिक इस्तेमाल की जाने वाली कक्षाओं में से एक ArrayList में किया जाता है। यह बहुत संभव है कि आपके मामले में जटिलता और कठोरता के उस स्तर की आवश्यकता न हो। लंबी कहानी छोटी, यदि सभी ensureCapacityInternalकॉल को कॉल से बदल दिया गया ensureCapacity, तो प्रदर्शन अभी भी वास्तव में, वास्तव में अच्छा होगा। यह एक सूक्ष्म दृष्टिकोण है जो शायद व्यापक विचार के बाद ही बना है।


3

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

सार्वजनिक विधियों में आमतौर पर "एक सुसंगत स्थिति में वस्तु को लेने, कुछ समझदार होने का एक अनुबंध होता है, वस्तु को एक (संभवतः अलग) सुसंगत" में छोड़ दें। यहाँ, "संगत" मतलब हो सकता है, उदाहरण के लिए, कि Lengthएक के List<T>अपने से अधिक नहीं है Capacityऔर उस से सूचकांक में तत्वों को संदर्भित0 करने के लिए Length-1फेंक नहीं होंगे।

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


1

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

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


-5

क्षमा करें, लेकिन अधिकांश अन्य 'हां आप कर सकते हैं' के जवाब से असहमत हैं और कहते हैं कि:

मैं एक सार्वजनिक विधि को दूसरे से कॉल करने वाले वर्ग को हतोत्साहित करता हूँ

इस अभ्यास के साथ कुछ संभावित मुद्दे हैं।

1: अनंत कक्षा में अनंत लूप

तो आपका बेस क्लास method1 से method2 को कॉल करता है, लेकिन फिर आप, या किसी और को, इसे इनहेरिट करते हैं और method1 को एक नए तरीके से छिपाते हैं, जो method2 को कॉल करता है।

2: इवेंट, लॉगिंग आदि।

उदाहरण के लिए मेरे पास एक तरीका है Add1 जो एक घटना को फायर करता है '1 जोड़ा!' मैं नहीं चाहता कि Add10 विधि उस घटना को बढ़ाए, एक लॉग या जो भी हो, दस बार लिखें।

3: थ्रेडिंग और अन्य गतिरोध

उदाहरण के लिए InsertComplexData एक db कनेक्शन खोलता है, एक लेन-देन शुरू करता है, एक टेबल को लॉक करता है, फिर InsertSimpleData को कॉल करता है, एक कनेक्शन खोलता है, एक लेनदेन शुरू करता है, टेबल के अनलॉक होने का इंतजार करता है ...।

मुझे यकीन है कि और भी कारण हैं, अन्य में से एक उत्तर 'आप मेथड 1 को संपादित करते हैं और हैरान हैं मेथड 2 अलग तरह से व्यवहार करना शुरू कर देता है'

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

संपादित करें ----

ओपी में विशिष्ट मामले पर विस्तार करते हैं।

हमारे पास बहुत अधिक विवरण नहीं है, लेकिन हम जानते हैं कि ReverseData को किसी प्रकार के एक इवेंट हैंडलर के साथ-साथ शेड्यूलट्रांसमीशन विधि द्वारा भी बुलाया जाता है।

मुझे लगता है कि रिवर्स डेटा भी वस्तु की आंतरिक स्थिति को बदलता है

इस मामले को देखते हुए मुझे लगता है कि थ्रेड सेफटी महत्वपूर्ण होगी और इसलिए अभ्यास पर मेरी तीसरी आपत्ति लागू होती है।

ReverseData धागे को सुरक्षित बनाने के लिए आप एक ताला जोड़ सकते हैं। लेकिन अगर ScheduleTransmission को थ्रेड सुरक्षित होना चाहिए तो आप उसी लॉक को साझा करना चाहेंगे।

ऐसा करने का सबसे आसान तरीका है कि ReverseData कोड को एक निजी विधि में स्थानांतरित करना और दोनों सार्वजनिक तरीकों को कॉल करना है। फिर आप लॉक स्टेटमेंट को पब्लिक मेथड्स में रख सकते हैं और लॉक ऑब्जेक्ट शेयर कर सकते हैं।

जाहिर है आप बहस कर सकते हैं "ऐसा कभी नहीं होगा!" या "मैं लॉक को दूसरे तरीके से प्रोग्राम कर सकता था" लेकिन अच्छी कोडिंग प्रैक्टिस के बारे में पहली जगह में अपने कोड को अच्छी तरह से स्ट्रक्चर करना है।

अकादमिक दृष्टि से मैं कहूंगा कि यह ठोस में एल का उल्लंघन करता है। सार्वजनिक तरीके केवल सार्वजनिक रूप से प्रशंसनीय से अधिक हैं। वे इसके उत्तराधिकारियों द्वारा भी परिवर्तनीय हैं। आपका कोड संशोधन के लिए बंद होना चाहिए जिसका अर्थ है कि आपको यह सोचना होगा कि आप सार्वजनिक और संरक्षित दोनों तरीकों से क्या काम करते हैं।

एक अन्य एक: आप भी संभावित DDD का उल्लंघन करते हैं। यदि आपका ऑब्जेक्ट एक डोमेन ऑब्जेक्ट है, तो इसकी सार्वजनिक विधियाँ डोमेन की शर्तें होनी चाहिए, जो व्यवसाय के लिए कुछ अर्थ रखती हैं। इस मामले में इसकी बहुत संभावना नहीं है कि 'एक दर्जन अंडे खरीदो' उसी तरह है जैसे '1 अंडा 12 बार खरीदें' भले ही वह इस तरह से शुरू हो।


3
ये मुद्दे ध्वनि सिद्धांत की सामान्य निंदा की तुलना में खराब विचार वाले वास्तुकला की तरह अधिक ध्वनि करते हैं। (शायद "1 जोड़ा" कॉलिंग हर बार ठीक है। यदि यह नहीं है, तो हमें अलग तरह से प्रोग्राम करना चाहिए। हो सकता है कि यदि दो विधियाँ एक ही संसाधन को एक्सक्लूसिव करने की कोशिश कर रही हों, तो उन्हें एक-दूसरे को कॉल नहीं करना चाहिए, या हमने उन्हें खराब तरीके से लिखा है। ।) वर्तमान में खराब कार्यान्वयन के बजाय , आप अधिक ठोस तर्कों पर ध्यान केंद्रित करना चाह सकते हैं जो एक सार्वभौमिक सिद्धांत के रूप में सार्वजनिक तरीकों तक पहुँचने से निपटते हैं। उदाहरण के लिए, अच्छा नीट कोड दिया गया , यह बुरा क्यों है?
doppelgreener 1

वास्तव में, यदि ऐसा नहीं है, तो आपके पास घटना डालने के लिए जगह नहीं है। इसी तरह # 3 के साथ सब कुछ ठीक नहीं है, अपने तरीके में से एक को नीचे चेन में लेन-देन की जरूरत है, तो आप खराब हैं
इवान

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

मेरे उदाहरण के शेयरों में एकमात्र कोड 'कोड' एक सार्वजनिक विधि है जिसमें दूसरे को कॉल किया जाता है। अगर आपको लगता है कि इसका 'बुरा कोड' अच्छा है! यह मेरा विवाद है
इवान

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