प्रोग्रामिंग भाषाएं चर और कार्यों के छायांकन / छिपने की अनुमति क्यों देती हैं?


31

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

मेरे लिए यह छिपाना और छायांकन को अस्वीकार करना बेहतर होगा।

क्या किसी को इन अवधारणाओं के अच्छे उपयोग का पता है?

अपडेट:
मैं कक्षा सदस्यों (निजी / संरक्षित सदस्यों) के एनकैप्सुलेशन का उल्लेख नहीं कर रहा हूं।


यही कारण है कि मेरे सभी फील्डनाम F
Pieter B

7
मुझे लगता है कि एरिक लिपर्ट ने इस पर एक अच्छा लेख लिखा था। ओह रुको, यहाँ यह है: blogs.msdn.com/b/ericlippert/archive/2008/05/21/…
Lescai Ionel

1
कृपया अपना प्रश्न स्पष्ट करें। क्या आप सामान्य रूप से छिपी जानकारी के बारे में पूछ रहे हैं, या लिप्टर्ट लेख में वर्णित विशिष्ट मामले के बारे में जहां एक व्युत्पन्न वर्ग आधार वर्ग के कार्यों को छुपाता है?
आरोन कुरत्झल्स

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

जवाबों:


26

यदि आप छुपाना और छाया करना बंद कर देते हैं, तो आपके पास क्या भाषा है जिसमें सभी चर वैश्विक हैं।

यह स्थानीय चर या कार्यों की अनुमति देने से स्पष्ट रूप से बदतर है जो वैश्विक चर या कार्यों को छिपा सकते हैं।

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

मौलिक मुद्दा छुपा / छाया नहीं है, बल्कि वैश्विक चर है।


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

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

@ThiagoSilva: और फिर आपकी भाषा में संकलक को यह बताने का एक तरीका होना चाहिए कि इस मॉड्यूल IS ने उस मॉड्यूल के "फ्रैमिस" चर को एक्सेस करने की अनुमति दी है। क्या आप किसी ऐसे व्यक्ति को छिपाने / छाया देने की अनुमति देते हैं जिसे वह जानता भी नहीं है, या क्या आप उसे इस बारे में बताने के लिए कहेंगे कि उसे उस नाम का उपयोग करने की अनुमति क्यों नहीं है?
जॉन आर। स्ट्रोहम

9
@Phil, मुझे आपसे असहमत होने का बहाना देता है, लेकिन ओपी ने "चर या कार्यों के छायांकन / छायांकन" और "अभिभावक", "बच्चा", "वर्ग" और "सदस्य" शब्दों के बारे में पूछा। ऐसा लगता है कि यह नाम के दायरे के बारे में एक सामान्य सवाल है।
जॉन आर। स्ट्रॉहम

3
@dylnmc, मुझे एक व्हिपसपैंपर युवा का सामना करने के लिए काफी समय तक जीने की उम्मीद नहीं थी कि एक स्पष्ट "2001: ए स्पेस ओडिसी" संदर्भ प्राप्त न हो।
जॉन आर। स्ट्रोम

15

क्या किसी को इन अवधारणाओं के अच्छे उपयोग का पता है?

सटीक, वर्णनात्मक पहचानकर्ताओं का उपयोग करना हमेशा एक अच्छा उपयोग होता है।

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

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

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


3
वास्तव में, नहीं, छिपाना और छायांकन को लागू करना आसान नहीं है। "सभी चर वैश्विक हैं" को लागू करना वास्तव में आसान है। आपको केवल एक नामस्थान की आवश्यकता है, और आप हमेशा नाम निर्यात करते हैं, जैसा कि कई नामस्थान रखने और प्रत्येक नाम के लिए तय करना है कि क्या इसे निर्यात करना है।
बजे जॉन आर। स्ट्रोह्म

5
@ JohnR.Strohm - ज़रूर, लेकिन जैसे ही आपके पास किसी भी तरह का स्कोप है (पढ़ें: क्लासेस) तो स्कोप्स को लोअर स्कॉप्स को छुपाना मुफ्त में है।
तेलस्टिन

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

@michaelshaw - बेशक, मुझे और अधिक स्पष्ट होना चाहिए था।
तेलेस्टाइन

7

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

इसकी अनुमति देने का संक्षिप्त उत्तर यह है कि ऐसा न करना डेवलपर्स को ऑब्जेक्ट-ओरिएंटेड डिज़ाइन के कई प्रमुख सिद्धांतों का उल्लंघन करने के लिए मजबूर करेगा।

यहाँ लंबा जवाब है; पहले, एक वैकल्पिक ब्रह्मांड में निम्न वर्ग संरचना पर विचार करें जहां C # सदस्य को छिपाने की अनुमति नहीं देता है:

public interface IFoo
{
   string MyFooString {get;}
   int FooMethod();
}

public class Foo:IFoo
{
   public string MyFooString {get{return "Foo";}}
   public int FooMethod() {//incredibly useful code here};
}

public class Bar:Foo
{
   //public new string MyFooString {get{return "Bar";}}
}

हम बार में सदस्य को असहज करना चाहते हैं, और ऐसा करके बार को एक अलग MyFooString प्रदान करने की अनुमति देते हैं। हालाँकि, हम ऐसा नहीं कर सकते क्योंकि यह सदस्य छिपने पर वैकल्पिक-वास्तविकता निषेध का उल्लंघन करेगा। यह विशेष रूप से कीड़े के लिए व्याप्त होगा और इस बात का एक प्रमुख उदाहरण है कि आप इसे प्रतिबंधित क्यों करना चाहते हैं; उदाहरण के लिए, यदि आपने निम्नलिखित कार्य किया तो आपको कौन सा कंसोल आउटपुट मिलेगा?

Bar myBar = new Bar();
Foo myFoo = myBar;
IFoo myIFoo = myFoo;

Console.WriteLine(myFoo.MyFooString);
Console.WriteLine(myBar.MyFooString);
Console.WriteLine(myIFoo.MyFooString);

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

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

public class Bar:Foo
{
   public string MyBarString {get{return "Bar";}}       
}

पूरी तरह से कानूनी, लेकिन यह वह व्यवहार नहीं है जो हम चाहते हैं। बार का एक उदाहरण हमेशा MyFooString संपत्ति के लिए "फू" का उत्पादन करेगा, जब हम चाहते थे कि यह "बार" का उत्पादन करे। न केवल हमें यह जानना होगा कि हमारा IFoo विशेष रूप से एक बार है, हमें अलग-अलग एक्सेसर का उपयोग करने के लिए भी जानना होगा।

हम भी, काफी प्रशंसनीय, माता-पिता के बच्चे के रिश्ते को भूल सकते हैं और सीधे इंटरफ़ेस को लागू कर सकते हैं:

public class Bar:IFoo
{
   public string MyFooString {get{return "Bar";}}
   public int FooMethod() {...}
}

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

public class Bar:IFoo
{
   public string MyFooString {get{return "Bar";}}
   private readonly theFoo = new Foo();

   public int FooMethod(){return theFoo.FooMethod();}
}

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

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

public class Foo:IFoo
{
   public virtual string MyFooString {get{return "Foo";}}
   //...
}

public class Bar:Foo
{
   public override string MyFooString {get{return "Bar";}}
}

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

सारांश में, सदस्य को छिपाना अनुमति देना इन बुराइयों का कम है। ऐसा नहीं होने से आम तौर पर ऑब्जेक्ट-ओरिएंटेड सिद्धांतों के खिलाफ होने वाले बदतर अत्याचारों को बढ़ावा दिया जा सकता है।


वास्तविक प्रश्न को संबोधित करने के लिए +1। सदस्य छिपने की एक वास्तविक दुनिया के उपयोग का एक अच्छा उदाहरण है IEnumerableऔर IEnumerable<T>इंटरफेस, एरिक Libbert के में वर्णित ब्लॉग पोस्ट विषय पर।
फिल

ओवरराइडिंग छुपा नहीं है । मैं @Phil से सहमत नहीं हूं कि यह प्रश्न को संबोधित करता है।
Jan Hudec

मेरा कहना था कि जब छिपाना कोई विकल्प न हो तो ओवरड्राइडिंग एक विकल्प होगा। मैं मानता हूं, यह छिपा नहीं है, और मैं पहले पैराग्राफ में बहुत कुछ कहता हूं। C # में छुपाने के मेरे वैकल्पिक-वास्तविकता परिदृश्य में कोई भी कार्यभार छिपा नहीं है; यही तो बात है।
कीथ

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

2

ईमानदारी से, सी # कंपाइलर टीम के सिद्धांत डेवलपर एरिक लिपर्ट इसे बहुत अच्छी तरह से समझाते हैं (लिंक के लिए धन्यवाद लेसकाई इओनेल)। नेट की IEnumerableऔर IEnumerable<T>इंटरफेस जब सदस्य छुपा उपयोगी है के अच्छे उदाहरण हैं।

.NET के शुरुआती दिनों में, हमारे पास जेनेरिक नहीं था। तो IEnumerableइंटरफ़ेस इस तरह देखा:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}

इस इंटरफ़ेस ने हमें foreachवस्तुओं के संग्रह पर अनुमति दी है , हालांकि हमें उन सभी वस्तुओं को सही तरीके से उपयोग करने के लिए डालना था।

फिर जेनरिक आए। जब हमें जेनेरिक मिला, तो हमें एक नया इंटरफ़ेस भी मिला:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}

जब हम उनके माध्यम से पुनरावृत्ति कर रहे हैं तो अब हमें वस्तुओं को डालना नहीं है! यहाँ प्रारंभ करें अब, यदि सदस्य को छिपाने की अनुमति नहीं थी, तो इंटरफ़ेस को कुछ इस तरह देखना होगा:

public interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> GetEnumeratorGeneric();
}

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

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


औपचारिक रूप से यह IEnumerable<T>.GetEnumerator()छुपाता है IEnumerable.GetEnumerator(), यह केवल इसलिए है क्योंकि ओवरराइड करते समय C # में सहसंयोजक वापसी प्रकार नहीं होते हैं। तार्किक रूप से यह एक ओवरराइड है, पूरी तरह से एलएसपी के अनुरूप है। छिपाना तब होता है जब आपके पास mapफ़ाइल में फ़ंक्शन में स्थानीय चर होता है using namespace std(C ++ में)।
Jan Hudec

2

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

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

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

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

एक विरासत की स्थिति में ओवरलोडिंग के बीच का अंतर जो इरादा है और जो एक बग है शब्दार्थ (अर्थ) है, इसलिए संकलक के पास यह जानने का कोई तरीका नहीं है कि आपने क्या किया या क्या सही है। एक सादे दायरे की स्थिति में, सही उत्तर हमेशा स्थानीय चीज़ होती है, इसलिए संकलक यह पता लगा सकता है कि सही चीज़ क्या है।

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


2

यहाँ मेरे दो सेंट हैं।

प्रोग्राम को ब्लॉक (कार्यों, प्रक्रियाओं) में संरचित किया जा सकता है जो प्रोग्राम लॉजिक की स्व-निहित इकाइयाँ हैं। प्रत्येक ब्लॉक नामों / पहचानकर्ताओं का उपयोग करके "चीजों" (चर, कार्यों, प्रक्रियाओं) को संदर्भित कर सकता है। नामों से चीजों की मैपिंग को बाइंडिंग कहा जाता है

ब्लॉक द्वारा उपयोग किए जाने वाले नाम तीन श्रेणियों में आते हैं:

  1. स्थानीय रूप से परिभाषित नाम, जैसे स्थानीय चर, जो केवल ब्लॉक के अंदर ही जाने जाते हैं।
  2. जब ब्लॉक लागू किया जाता है, तो मूल्यों को बाध्य किया जाता है और ब्लॉक के इनपुट / आउटपुट पैरामीटर को निर्दिष्ट करने के लिए कॉलर द्वारा उपयोग किया जा सकता है।
  3. बाहरी नाम / बाइंडिंग जो उस वातावरण में परिभाषित होते हैं जिसमें ब्लॉक समाहित है और ब्लॉक के दायरे में हैं।

उदाहरण के लिए निम्नलिखित सी कार्यक्रम पर विचार करें

#include<stdio.h>

void print_double_int(int n)
{
  int d = n * 2;

  printf("%d\n", d);
}

int main(int argc, char *argv[])
{
  print_double_int(4);
}

फ़ंक्शन print_double_intका एक स्थानीय नाम (स्थानीय चर) d, और तर्क है n, और बाहरी, वैश्विक नाम का उपयोग करता है printf, जो गुंजाइश में है लेकिन स्थानीय रूप से परिभाषित नहीं है।

नोटिस जो printfएक तर्क के रूप में पारित किया जा सकता है:

#include<stdio.h>

void print_double_int(int n, int printf(const char *, ...))
{
  int d = n * 2;

  printf("%d\n", d);
}

int main(int argc, char *argv[])
{
  print_double_int(4, printf);
}

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

बाहरी रूप से परिभाषित नामों का एक और समान उपयोग क्लोजर में पाया जा सकता है। इस स्थिति में एक ब्लॉक के शाब्दिक संदर्भ में परिभाषित नाम का उपयोग ब्लॉक के भीतर किया जा सकता है, और जब तक ब्लॉक इसे संदर्भित करता है, तब तक (आमतौर पर) उस नाम से जुड़ा मूल्य मौजूद रहेगा।

उदाहरण के लिए इस स्काला कोड को लें:

object ClosureExample
{
  def createMultiplier(n: Int) = (m: Int) => m * n

  def main(args: Array[String])
  {
    val multiplier3 = createMultiplier(3)
    val multiplier5 = createMultiplier(5)

    // Prints 6.
    println(multiplier3(2))

    // Prints 10.
    println(multiplier5(2))
  }
}

फ़ंक्शन createMultiplierका वापसी मान क्लोजर है (m: Int) => m * n, जिसमें तर्क mऔर बाहरी नाम शामिल हैं n। नाम nउस संदर्भ को देखकर हल किया जाता है जिसमें क्लोजर को परिभाषित किया गया है: नाम nफ़ंक्शन के तर्क से बंधा हुआ है createMultiplier। ध्यान दें कि यह बंधन तब बनाया जाता है जब क्लोजर बनाया जाता है, अर्थात जब createMultiplierइनवॉइस किया जाता है। इसलिए नाम nफ़ंक्शन के किसी विशेष आह्वान के लिए एक तर्क के वास्तविक मूल्य के लिए बाध्य है। लाइब्रेरी फ़ंक्शन के मामले के साथ इसका विरोध करें printf, जो प्रोग्राम के निष्पादन योग्य होने पर लिंकर द्वारा हल किया जाता है।

संक्षेप में, यह कोड के एक स्थानीय ब्लॉक के अंदर बाहरी नामों को संदर्भित करने के लिए उपयोगी हो सकता है ताकि आप

  • बाहरी रूप से परिभाषित नामों को तर्क के रूप में स्पष्ट रूप से पारित करने की आवश्यकता नहीं है
  • जब आप कोई ब्लॉक बनाते हैं, तो आप रनटाइम पर बाइंडिंग को फ्रीज कर सकते हैं, और बाद में ब्लॉक का उपयोग करने पर इसे एक्सेस कर सकते हैं।

शैडोइंग तब आती है जब आप समझते हैं कि एक ब्लॉक में आप केवल प्रासंगिक नामों में रुचि रखते हैं जो पर्यावरण में परिभाषित हैं, उदाहरण के लिए उस printfफ़ंक्शन में जिसे आप उपयोग करना चाहते हैं। (संयोग से यदि आप एक स्थानीय नाम उपयोग करना चाहते हैं getc, putc, scanf, ...) पहले से ही वातावरण में इस्तेमाल किया गया है, आप सरल अभाव की अनदेखी करने के (छाया) वैश्विक नाम। इसलिए, जब आप स्थानीय स्तर पर सोचते हैं, तो आप पूरे (संभवतः बहुत बड़े) संदर्भ पर विचार नहीं करना चाहते हैं।

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

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

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