"बाइंडिंग" एक विधि नाम को अमूल्य कोड के एक टुकड़े पर हल करने के अधिनियम को संदर्भित करता है। आमतौर पर, फ़ंक्शन कॉल को संकलन समय पर या लिंक समय पर हल किया जा सकता है। स्थैतिक बंधन का उपयोग करने वाली भाषा का एक उदाहरण C है:
int foo(int x);
int main(int, char**) {
printf("%d\n", foo(40));
return 0;
}
int foo(int x) { return x + 2; }
यहां, कॉल foo(40)
को कंपाइलर द्वारा हल किया जा सकता है। यह शुरुआती कुछ अनुकूलन जैसे कि इनलाइनिंग की अनुमति देता है। सबसे महत्वपूर्ण लाभ हैं:
- हम टाइप चेकिंग कर सकते हैं
- हम अनुकूलन कर सकते हैं
दूसरी ओर, कुछ भाषाएं अंतिम संभव समय में फ़ंक्शन रिज़ॉल्यूशन को स्थगित कर देती हैं। एक उदाहरण पायथन है, जहां हम मक्खी पर प्रतीकों को फिर से परिभाषित कर सकते हैं:
def foo():
""""call the bar() function. We have no idea what bar is."""
return bar()
def bar():
return 42
print(foo()) # bar() is 42, so this prints "42"
# use reflection to overwrite the "bar" variable
locals()["bar"] = lambda: "Hello World"
print(foo()) # bar() was redefined to "Hello World", so it prints that
bar = 42
print(foo()) # throws TypeError: 'int' object is not callable
यह देर से बाध्यकारी का एक उदाहरण है। हालांकि यह कठोर प्रकार की जाँच को अनुचित तरीके से करता है (प्रकार की जाँच केवल रनटाइम पर की जा सकती है), यह कहीं अधिक लचीली है और हमें उन अवधारणाओं को व्यक्त करने की अनुमति देती है जो स्थिर टाइपिंग या प्रारंभिक बाइंडिंग के दायरे में व्यक्त नहीं की जा सकती हैं। उदाहरण के लिए, हम रनटाइम पर नए कार्य जोड़ सकते हैं।
आमतौर पर "स्थिर" रूप में लागू की जाने वाली विधि प्रेषण इन दो चरम सीमाओं के बीच ओओपी भाषा है: एक वर्ग सामने वाले सभी समर्थित कार्यों के प्रकार की घोषणा करता है, इसलिए इन्हें सांख्यिकीय रूप से जाना जाता है और टाइपकास्ट किया जा सकता है। फिर हम एक साधारण लुकअप टेबल (VTable) बना सकते हैं जो वास्तविक कार्यान्वयन की ओर इशारा करती है। प्रत्येक ऑब्जेक्ट में एक वाइबेटर के लिए एक पॉइंटर होता है। प्रकार प्रणाली यह गारंटी देती है कि हमें प्राप्त होने वाली किसी भी वस्तु में एक उपयुक्त व्यवहार्यता होगी, लेकिन हमें इस समय का कोई भी विचार नहीं है कि इस घड़ी का मूल्य क्या है। इसलिए, ऑब्जेक्ट्स को डेटा के रूप में कार्यों को पास करने के लिए इस्तेमाल किया जा सकता है (आधे कारण ओओपी और फ़ंक्शन प्रोग्रामिंग समतुल्य हैं)। Vtables को किसी भी भाषा में आसानी से लागू किया जा सकता है जो फ़ंक्शन पॉइंटर्स का समर्थन करता है, जैसे कि C।
#define METHOD_CALL(object_ptr, name, ...) \
(object_ptr)->vtable->name((object_ptr), __VA_ARGS__)
typedef struct {
void (*sayHello)(const MyObject* this, const char* yourname);
} MyObject_VTable;
typedef struct {
const MyObject_VTable* vtable;
const char* name;
} MyObject;
static void MyObject_sayHello_normal(const MyObject* this, const char* yourname) {
printf("Hello %s, I'm %s!\n", yourname, this->name);
}
static void MyObject_sayHello_alien(const MyObject* this, const char* yourname) {
printf("Greetings, %s, we are the %s!\n", yourname, this->name);
}
static MyObject_VTable MyObject_VTable_normal = {
.sayHello = MyObject_sayHello_normal,
};
static MyObject_VTable MyObject_VTable_alien = {
.sayHello = MyObject_sayHello_alien,
};
static void sayHelloToMeredith(const MyObject* greeter) {
// we have no idea what the VTable contents of my object are.
// However, we do know it has a sayHello method.
// This is dynamic dispatch right here!
METHOD_CALL(greeter, sayHello, "Meredith");
}
int main() {
// two objects with different vtables
MyObject frank = { .vtable = &MyObject_VTable_normal, .name = "Frank" };
MyObject zorg = { .vtable = &MyObject_VTable_alien, .name = "Zorg" };
sayHelloToMeredith(&frank); // prints "Hello Meredith, I'm Frank!"
sayHelloToMeredith(&zorg); // prints "Greetings, Meredith, we are the Zorg!"
}
इस तरह की विधि खोज को "डायनेमिक प्रेषण" के रूप में भी जाना जाता है, और कहीं-कहीं शुरुआती बाध्यकारी और देर से बाध्यकारी के बीच भी। मैं गतिशील विधि प्रेषण को OOP प्रोग्रामिंग की केंद्रीय परिभाषित करने वाली संपत्ति मानता हूं, कुछ भी (जैसे एनकैप्सुलेशन, सबटिपिंग, ...) माध्यमिक होने के लिए। यह हमें हमारे कोड में बहुरूपता का परिचय देने में सक्षम बनाता है, और यहां तक कि इसे फिर से जोड़ने के बिना कोड के एक टुकड़े में नया व्यवहार जोड़ने के लिए! C उदाहरण में, कोई भी एक नया व्यवहार्य जोड़ सकता है और उस vtable के साथ किसी ऑब्जेक्ट को पास कर सकता है sayHelloToMeredith()
।
हालांकि यह देर-ईश बाध्यकारी है, यह Kay द्वारा इष्ट "चरम देर से बाध्यकारी" नहीं है। वैचारिक मॉडल "फ़ंक्शन पॉइंटर के माध्यम से प्रेषण" के बजाय, वह "मैसेज पासिंग के माध्यम से प्रेषण प्रेषण" का उपयोग करता है। यह एक महत्वपूर्ण अंतर है क्योंकि संदेश गुजरना कहीं अधिक सामान्य है। इस मॉडल में, प्रत्येक ऑब्जेक्ट में एक इनबॉक्स होता है जहां अन्य ऑब्जेक्ट संदेश डाल सकते हैं। प्राप्त वस्तु तब उस संदेश की व्याख्या करने की कोशिश कर सकती है। सबसे प्रसिद्ध ओओपी प्रणाली डब्ल्यूडब्ल्यूडब्ल्यू है। यहां, संदेश HTTP अनुरोध हैं, और सर्वर ऑब्जेक्ट हैं।
उदाहरण के लिए, मैं programmers.stackexchange.se सर्वर से पूछ सकता हूं GET /questions/301919/
। इसकी तुलना नोटेशन से करें programmers.get("/questions/301919/")
। सर्वर इस अनुरोध को अस्वीकार कर सकता है या मुझे एक त्रुटि वापस भेज सकता है, या यह मुझे आपके प्रश्न की सेवा दे सकता है।
संदेश पास करने की शक्ति यह है कि यह बहुत अच्छी तरह से मापता है: कोई भी डेटा साझा नहीं किया जाता है (केवल स्थानांतरित), सब कुछ अतुल्यकालिक रूप से हो सकता है, और ऑब्जेक्ट संदेशों की व्याख्या कर सकते हैं, हालांकि वे पसंद करते हैं। यह एक संदेश OOP प्रणाली को आसानी से विस्तार योग्य बनाता है। मैं ऐसे संदेश भेज सकता हूं जो हर कोई नहीं समझ सकता है, और या तो अपना अपेक्षित परिणाम या एक त्रुटि वापस पा सकता है। ऑब्जेक्ट को सामने वाले को घोषित करने की आवश्यकता नहीं है कि वह किन संदेशों का जवाब देगा।
यह एक संदेश के रिसीवर पर शुद्धता बनाए रखने की जिम्मेदारी डालता है, एक विचार जिसे एनकैप्सुलेशन के रूप में भी जाना जाता है। उदाहरण के लिए, मैं बिना HTTP संदेश के किसी HTTP सर्वर से फाइल नहीं पढ़ सकता। यह HTTP सर्वर को मेरे अनुरोध को अस्वीकार करने की अनुमति देता है, उदाहरण के लिए यदि मेरे पास अनुमति नहीं है। छोटे पैमाने पर OOP, इसका मतलब है कि मेरे पास किसी वस्तु की आंतरिक स्थिति तक पढ़ने-लिखने की पहुंच नहीं है, लेकिन सार्वजनिक तरीकों से जाना चाहिए। एक HTTP सर्वर के लिए मुझे एक फ़ाइल की सेवा करने की आवश्यकता नहीं है। यह गतिशील रूप से डीबी से उत्पन्न सामग्री हो सकती है। वास्तविक OOP में, उपयोगकर्ता द्वारा सूचना के बिना किसी वस्तु के संदेशों के प्रति प्रतिक्रिया का तंत्र कैसे स्विच किया जा सकता है। यह "प्रतिबिंब" से अधिक मजबूत है, लेकिन आमतौर पर एक पूर्ण मेटा-ऑब्जेक्ट प्रोटोकॉल है। ऊपर मेरा C उदाहरण रनटाइम में प्रेषण तंत्र को नहीं बदल सकता है।
प्रेषण तंत्र को बदलने की क्षमता देर से बाध्यकारी है, क्योंकि सभी संदेश उपयोगकर्ता-निश्चित कोड के माध्यम से रूट किए जाते हैं। और यह अत्यंत शक्तिशाली है: एक मेटा-ऑब्जेक्ट प्रोटोकॉल दिया गया है, मैं इसमें क्लास, प्रोटोटाइप, इनहेरिटेंस, एब्स्ट्रैक्ट क्लास, इंटरफेस, लक्षण, मल्टीपल इनहेरिटेंस, मल्टी-डिस्पैच, पहलू-उन्मुख प्रोग्रामिंग, रिफ्लेक्शन, रिमोट मेथड इनवोकेशन जैसे फीचर्स जोड़ सकता हूं। किसी भाषा की छद्म वस्तुएं आदि जो इन विशेषताओं के साथ शुरू नहीं होती हैं। विकसित करने की यह शक्ति अधिक स्थिर भाषाओं जैसे C #, Java, या C ++ से पूरी तरह से अनुपस्थित है।