การผูกปลายเชิงวัตถุ


11

ในคำจำกัดความของอลันเคย์ที่เน้นวัตถุมีคำจำกัดความนี้บางส่วนที่ฉันไม่เข้าใจ:

OOP สำหรับฉันหมายถึงเพียงการส่งข้อความการเก็บรักษาในท้องถิ่นและการป้องกันและการซ่อนกระบวนการของรัฐและการผูกมัดขั้นสุดท้ายของทุกสิ่ง

แต่ "LateBinding" หมายถึงอะไร ฉันจะใช้สิ่งนี้กับภาษาอย่าง C # ได้อย่างไร และทำไมเรื่องนี้สำคัญมาก



2
OOP ใน C # อาจไม่ใช่ OOP Alan Kay ที่มีอยู่ในใจ
Doc Brown

ฉันเห็นด้วยกับคุณอย่างแน่นอน ... ตัวอย่างยินดีต้อนรับในทุกภาษา
Luca Zulian

คำตอบ:


14

“ การผูก” หมายถึงการกระทำของการแก้ไขชื่อวิธีการกับชิ้นส่วนของรหัสที่เรียกคืนไม่ได้ โดยทั่วไปการเรียกใช้ฟังก์ชันสามารถแก้ไขได้ในเวลารวบรวมหรือเวลาลิงก์ ตัวอย่างของภาษาที่ใช้การรวมแบบสแตติกคือ 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)คอมไพเลอร์สามารถแก้ไขได้ ต้นนี้ช่วยให้การเพิ่มประสิทธิภาพบางอย่างเช่นอินไลน์ ข้อดีที่สำคัญที่สุดคือ:

  • เราสามารถทำการตรวจสอบประเภท
  • เราสามารถทำการเพิ่มประสิทธิภาพ

ในบางภาษาอาจเลื่อนความละเอียดของฟังก์ชั่นเป็นวินาทีสุดท้าย ตัวอย่างคือ Python ที่เราสามารถกำหนดสัญลักษณ์ใหม่ได้ทันที:

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

นี่คือตัวอย่างของการผูกปลาย ในขณะที่มันทำการตรวจสอบอย่างเข้มงวดโดยไม่มีเหตุผล (การตรวจสอบประเภทสามารถทำได้ที่รันไทม์เท่านั้น) แต่มันมีความยืดหยุ่นมากกว่าและทำให้เราสามารถแสดงแนวคิดที่ไม่สามารถแสดงออกได้ภายในขอบเขตของการพิมพ์แบบสแตติกหรือการรวมก่อนหน้า ตัวอย่างเช่นเราสามารถเพิ่มฟังก์ชั่นใหม่ที่รันไทม์

วิธีการจัดส่งตามที่ใช้กันทั่วไปในภาษา OOP "คงที่" อยู่ที่ไหนสักแห่งระหว่างสองขั้ว: คลาสประกาศประเภทของการดำเนินการที่รองรับทั้งหมดล่วงหน้าดังนั้นสิ่งเหล่านี้จึงเป็นที่รู้จักและสามารถพิมพ์ได้ จากนั้นเราสามารถสร้างตารางการค้นหาอย่างง่าย (VTable) ที่ชี้ไปยังการใช้งานจริง แต่ละวัตถุมีตัวชี้ไปยัง vtable ระบบประเภทรับประกันว่าวัตถุใด ๆ ที่เราได้รับจะมี vtable ที่เหมาะสม แต่เราไม่มีความคิดในเวลารวบรวมว่ามูลค่าของตารางการค้นหานี้คืออะไร ดังนั้นวัตถุสามารถใช้ในการส่งผ่านฟังก์ชั่นรอบ ๆ เป็นข้อมูล (ครึ่งเหตุผลว่าทำไม OOP และฟังก์ชั่นการเขียนโปรแกรมเทียบเท่า) 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 ชื่นชอบ แทนที่จะเป็นรูปแบบแนวคิด“ วิธีการแจกจ่ายผ่านตัวชี้ฟังก์ชั่น” เขาใช้“ วิธีการส่งผ่านข้อความผ่าน” นี่คือความแตกต่างที่สำคัญเพราะการส่งข้อความเป็นเรื่องทั่วไปมากขึ้น ในรุ่นนี้แต่ละวัตถุมีกล่องจดหมายเข้าซึ่งวัตถุอื่นสามารถใส่ข้อความได้ วัตถุที่รับนั้นสามารถลองตีความข้อความนั้นได้ ระบบ OOP ที่เป็นที่รู้จักมากที่สุดคือ WWW ที่นี่ข้อความเป็นคำขอ HTTP และเซิร์ฟเวอร์เป็นวัตถุ

GET /questions/301919/ตัวอย่างเช่นผมสามารถขอเซิร์ฟเวอร์ programmers.stackexchange.se programmers.get("/questions/301919/")เปรียบเทียบนี้เพื่อสัญกรณ์ เซิร์ฟเวอร์สามารถปฏิเสธคำขอนี้หรือส่งข้อผิดพลาดกลับมาให้ฉันหรือให้ฉันตอบคำถามของคุณ

พลังของการส่งข้อความคือมันปรับขนาดได้ดีมาก: ไม่มีการแชร์ข้อมูล (ถ่ายโอนเท่านั้น) ทุกอย่างสามารถเกิดขึ้นแบบอะซิงโครนัสและวัตถุสามารถตีความข้อความได้ตามต้องการ สิ่งนี้ทำให้ข้อความที่ส่งผ่านระบบ OOP สามารถขยายได้อย่างง่ายดาย ฉันสามารถส่งข้อความที่ทุกคนไม่เข้าใจและรับผลที่คาดหวังหรือข้อผิดพลาดกลับมา วัตถุไม่จำเป็นต้องประกาศล่วงหน้าว่าจะตอบกลับข้อความใด

เรื่องนี้ทำให้ความรับผิดชอบในการรักษาความถูกต้องไปยังผู้รับข้อความความคิดที่รู้จักกันว่า encapsulation เช่นฉันไม่สามารถอ่านไฟล์จากเซิร์ฟเวอร์ HTTP โดยไม่ต้องขอผ่านทางข้อความ HTTP สิ่งนี้ทำให้เซิร์ฟเวอร์ HTTP ปฏิเสธคำขอของฉันเช่นหากฉันไม่มีสิทธิ์ ในขนาดที่เล็กกว่า OOP นี่หมายความว่าฉันไม่มีสิทธิ์อ่าน - เขียนสำหรับสถานะภายในของวัตถุ แต่ต้องผ่านวิธีการสาธารณะ เซิร์ฟเวอร์ HTTP ไม่จำเป็นต้องให้บริการไฟล์ด้วยเช่นกัน มันอาจจะสร้างเนื้อหาแบบไดนามิกจากฐานข้อมูล ใน OOP จริงกลไกของวิธีการที่วัตถุตอบสนองต่อข้อความสามารถเปลี่ยนได้โดยไม่ต้องแจ้งให้ผู้ใช้ทราบ สิ่งนี้แข็งแกร่งกว่า "การสะท้อน" แต่โดยทั่วไปแล้วจะเป็นโปรโตคอลเมตาวัตถุ ตัวอย่าง C ของฉันด้านบนไม่สามารถเปลี่ยนกลไกการส่งที่รันไทม์

ความสามารถในการเปลี่ยนกลไกการจัดส่งบ่งบอกถึงการผูกปลายเนื่องจากข้อความทั้งหมดจะถูกส่งผ่านรหัสที่ผู้ใช้กำหนดได้ และนี่เป็นสิ่งที่ทรงพลังอย่างยิ่ง: เนื่องจากโปรโตคอลเมตาวัตถุฉันสามารถเพิ่มคุณสมบัติเช่นคลาส, ต้นแบบ, การสืบทอด, คลาสนามธรรม, อินเตอร์เฟส, ลักษณะ, การสืบทอดหลาย, การเขียนโปรแกรมเชิงกว้าง, การสะท้อน, การเรียกใช้วิธีการจากระยะไกล วัตถุพรอกซี ฯลฯ เพื่อภาษาที่ไม่ได้เริ่มต้นด้วยคุณสมบัติเหล่านี้ พลังในการพัฒนานี้ขาดหายไปอย่างสิ้นเชิงจากภาษาแบบคงที่เช่น C #, Java หรือ C ++


4

การรวมภายหลังหมายถึงวิธีการที่วัตถุสื่อสารกัน อุดมคติที่อลันกำลังพยายามทำให้สำเร็จคือการที่วัตถุจะอยู่คู่กันอย่างอิสระมากที่สุด กล่าวอีกนัยหนึ่งว่าวัตถุจำเป็นต้องรู้ค่าต่ำสุดที่เป็นไปได้เพื่อที่จะสื่อสารกับวัตถุอื่น

ทำไม? เพราะนั่นกระตุ้นให้เกิดความสามารถในการเปลี่ยนส่วนต่าง ๆ ของระบบอย่างอิสระและกำบังให้มันเติบโตและเปลี่ยนแปลงแบบอินทรีย์

ยกตัวอย่างเช่นใน C # คุณอาจเขียนในวิธีการบางอย่างเช่นobj1 obj2.doSomething()คุณสามารถดูเกี่ยวกับเรื่องนี้เป็นการสื่อสารกับobj1 obj2เพื่อให้สิ่งนี้เกิดขึ้นใน C # คุณobj1จำเป็นต้องรู้อะไรobj2บ้าง มันจะต้องรู้ระดับของมัน มันจะตรวจสอบว่าคลาสมีเมธอดที่เรียกว่าdoSomethingและมีเวอร์ชันของเมธอดนั้นที่รับพารามิเตอร์ศูนย์

ตอนนี้ลองจินตนาการถึงระบบที่คุณกำลังส่งข้อความผ่านเครือข่ายหรือคล้ายกัน Runtime.sendMsg(ipAddress, "doSomething")คุณอาจจะเขียนสิ่งที่ต้องการ ในกรณีนี้คุณไม่จำเป็นต้องรู้อะไรมากมายเกี่ยวกับเครื่องที่คุณกำลังสื่อสารด้วย; มันน่าจะสามารถติดต่อผ่านทาง IP และจะทำบางสิ่งเมื่อได้รับสตริง "doSomething" แต่อย่างอื่นคุณก็รู้น้อยมาก

ทีนี้ลองนึกภาพว่าวัตถุสื่อสารกันอย่างไร คุณรู้ที่อยู่และคุณสามารถส่งข้อความไปยังที่อยู่นั้นด้วยฟังก์ชัน "ตู้ไปรษณีย์" บางประเภท ในกรณีobj1นี้ไม่จำเป็นต้องรู้มากนักobj2เพียงแค่เป็นที่อยู่ doSomethingมันไม่จำเป็นที่จะต้องรู้ว่ามันเข้าใจ

นั่นเป็นจุดเริ่มต้นของการผูกปลาย ตอนนี้ในภาษาที่ใช้เช่น Smalltalk และ ObjectiveC ปกติแล้วจะมีบิตของน้ำตาลประโยคเพื่อซ่อนฟังก์ชั่นตู้ไปรษณีย์ แต่อย่างอื่นความคิดก็เหมือนกัน

ใน C # คุณสามารถทำซ้ำเรียงโดยมีRuntimeคลาสที่ยอมรับการอ้างอิงวัตถุและสตริงและใช้การสะท้อนเพื่อค้นหาวิธีการและเรียกมัน (มันจะเริ่มซับซ้อนด้วยการขัดแย้งและค่ากลับ แต่มันจะเป็นไปได้แม้ว่า น่าเกลียด)

แก้ไข: เพื่อบรรเทาความสับสนเกี่ยวกับความหมายของการผูกปลาย ในคำตอบนี้ฉันหมายถึงการผูกมัดตอนปลายเพราะฉันเข้าใจ Alan Kay หมายถึงมันและนำไปใช้ใน Smalltalk มันไม่ได้เป็นคำที่ใช้กันทั่วไปและทันสมัยซึ่งโดยทั่วไปหมายถึงการจัดส่งแบบไดนามิก หลังครอบคลุมความล่าช้าในการแก้ไขวิธีการที่แน่นอนจนกว่าจะถึงเวลารันไทม์ แต่ยังคงต้องการข้อมูลประเภทบางอย่างสำหรับผู้รับในเวลารวบรวม

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.