มรดกต้นแบบแตกต่างจากมรดกดั้งเดิมอย่างไร


27

การสืบทอดความหลากหลายและการห่อหุ้มเป็นคุณลักษณะที่สำคัญที่สุดสามประการของ OOP และจากนั้นการสืบทอดมีสถิติการใช้งานที่สูงในทุกวันนี้ ฉันกำลังเรียนรู้จาวาสคริปต์และที่นี่พวกเขาทุกคนบอกว่ามันมีมรดกต้นแบบและผู้คนทุกที่บอกว่ามันเป็นสิ่งที่แตกต่างจากมรดกคลาสสิก

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

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

คำตอบ:


6

โพสต์บล็อกล่าสุดเกี่ยวกับ JS OO

ฉันเชื่อว่าสิ่งที่คุณเปรียบเทียบคือการจำลอง OO แบบดั้งเดิมใน JavaScriptและOO แบบคลาสสิกและแน่นอนว่าคุณไม่สามารถเห็นความแตกต่างได้

คำเตือน:แทนที่การอ้างอิงทั้งหมดเพื่อ "prototypal OO" ด้วย "prototypal OO ใน JavaScript" ฉันไม่รู้ลักษณะเฉพาะของตนเองหรือการนำไปใช้อย่างอื่น

อย่างไรก็ตามต้นแบบ OO นั้นแตกต่างกัน ด้วยต้นแบบที่คุณมีเพียงวัตถุและคุณสามารถฉีดวัตถุเข้าไปในห่วงโซ่ต้นแบบวัตถุอื่น ๆ เมื่อใดก็ตามที่คุณเข้าถึงคุณสมบัติบนวัตถุที่คุณค้นหาวัตถุนั้นและวัตถุใด ๆ ในห่วงโซ่ต้นแบบ

สำหรับต้นแบบ OO นั้นไม่มีแนวคิดเรื่องการห่อหุ้ม การห่อหุ้มเป็นคุณลักษณะของขอบเขตการปิดและฟังก์ชันชั้นหนึ่ง แต่ไม่มีส่วนเกี่ยวข้องกับต้นแบบ OO นอกจากนี้ยังมีความคิดเกี่ยวกับการสืบทอดไม่มีสิ่งที่ผู้คนเรียกว่า "การสืบทอด" จริง ๆ แล้วเป็นเพียงความหลากหลาย

อย่างไรก็ตามมันมีความหลากหลาย

ตัวอย่างเช่น

var Dog = {
  walk: function() { console.log("walks"); }
}

var d = Object.create(Dog);
d.walk();

เห็นdได้ชัดว่ามีการเข้าถึงวิธีการDog.walkและสิ่งนี้จัดแสดงความแตกต่าง

ดังนั้นจริงมีความแตกต่างใหญ่ คุณมีความหลากหลาย

อย่างไรก็ตามตามที่กล่าวไว้ถ้าคุณต้องการที่จะทำเช่นนั้น (ฉันไม่รู้ว่าทำไมคุณจะทำ) คุณสามารถเลียนแบบ OO แบบดั้งเดิมใน JavaScriptและเข้าถึงการห่อหุ้มและการสืบทอด (จำกัด )


1
@Rayons, Google สำหรับการถ่ายทอดทางพันธุกรรมต้นแบบและคุณจะเห็นว่าการถ่ายทอดทางพันธุกรรมต้นแบบขึ้นมา แม้จาก Yahoo!
Saeed Neamati

@Aeed มรดกเป็นคำที่คลุมเครือและใช้ในทางที่ผิด สิ่งที่พวกเขาหมายถึงด้วย "มรดก" ฉันติดป้าย "polymorphism" คำจำกัดความที่คลุมเครือเกินไป
Raynos

ไม่มี @Rayons ผมหมายถึงคำprototypalเป็นคำที่ถูกต้องไม่แม่บท ฉันไม่ได้พูดเกี่ยวกับการรับมรดก :)
Saeed Neamati

1
@Seed นั่นเป็นหนึ่งในความผิดพลาดที่เกิดขึ้นในใจของฉัน ฉันไม่สนใจเลย
Raynos

1
อืม .. คำศัพท์ ick ฉันจะเรียกวิธีการที่วัตถุจาวาสคริปต์เกี่ยวข้องกับต้นแบบ "การมอบหมาย" ของพวกเขา ความแตกต่างดูเหมือนว่าฉันจะเป็นความกังวลในระดับต่ำกว่าตามความจริงที่ว่าชื่อที่กำหนดอาจชี้ไปที่รหัสที่แตกต่างกันสำหรับวัตถุที่แตกต่างกัน
Sean McMillan

15

การสืบทอดแบบคลาสสิกสืบทอดพฤติกรรมโดยไม่มีสถานะใด ๆ จากคลาสผู้ปกครอง มันสืบทอดพฤติกรรมในขณะที่วัตถุนั้นถูกยกตัวอย่าง

Prototypal ที่สืบทอดสืบทอดพฤติกรรมและสถานะจากวัตถุหลัก มันสืบทอดพฤติกรรมและสถานะในขณะที่วัตถุถูกเรียก เมื่อวัตถุแม่เปลี่ยนแปลงในเวลาทำงานสถานะและพฤติกรรมของวัตถุลูกจะได้รับผลกระทบ

"ข้อได้เปรียบ" ของการรับมรดกต้นแบบคือคุณสามารถ "แก้ไข" สถานะและพฤติกรรมหลังจากวัตถุทั้งหมดของคุณมีการยกตัวอย่าง ตัวอย่างเช่นในเฟรมเวิร์ก Ext JS เป็นเรื่องปกติที่จะโหลด "การแทนที่" ที่ปะส่วนประกอบหลักของเฟรมเวิร์กหลังจากเฟรมเวิร์กได้รับอินสแตนซ์


4
ในทางปฏิบัติหากคุณกำลังสืบทอดสถานะคุณกำลังตั้งตัวเองเพื่อโลกแห่งความเจ็บปวด รัฐที่สืบทอดจะถูกแบ่งปันหากอยู่บนวัตถุอื่น
Sean McMillan

1
มันเกิดขึ้นกับฉันว่าในจาวาสคริปต์ไม่มีแนวคิดของพฤติกรรมที่แยกจากรัฐ นอกเหนือจากฟังก์ชั่นคอนสตรัคพฤติกรรมทั้งหมดสามารถกำหนด / แก้ไขการโพสต์วัตถุที่สร้างเช่นเดียวกับสถานะอื่น
Joeri Sebrechts

ดังนั้น Python ไม่มีการสืบทอดแบบดั้งเดิม? ถ้าผมมีclass C(object): def m(self, x): return x*2แล้วinstance = C()แล้วเมื่อฉันเรียกฉันได้รับinstance.m(3) 6แต่ถ้าผมแล้วเปลี่ยนCเพื่อให้C.m = lambda s, x: x*xและผมทำงานตอนนี้ผมได้รับinstance.m(3) 9เช่นเดียวกันถ้าฉันทำclass D(C)และเปลี่ยนวิธีในCกรณีใด ๆ ของการDรับวิธีการเปลี่ยนแปลงเช่นกัน ฉันเข้าใจผิดหรือว่านี่หมายความว่า Python ไม่มีการสืบทอดคลาสสิกตามคำจำกัดความของคุณ?
mVChr

@mVChr: ​​คุณยังคงสืบทอดพฤติกรรมและไม่ได้ระบุในกรณีนั้นซึ่งชี้ไปที่การสืบทอดแบบคลาสสิก แต่มันชี้ไปที่ปัญหาที่มีคำจำกัดความของฉันที่ว่า "สืบทอดพฤติกรรมในขณะที่วัตถุนั้นถูกสร้างขึ้น" ฉันไม่ค่อยแน่ใจว่าจะแก้ไขคำจำกัดความอย่างไร
Joeri Sebrechts

9

ครั้งแรก: ส่วนใหญ่แล้วคุณจะใช้วัตถุไม่ได้กำหนดและการใช้วัตถุเหมือนกันภายใต้กระบวนทัศน์ทั้งสอง

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

ประการที่สาม: ลักษณะแบบไดนามิกของ Javascript มีอิทธิพลมากขึ้นกว่าการสืบทอด ความจริงที่ว่าฉันสามารถเพิ่มวิธีการใหม่ให้กับทุกประเภทของประเภทโดยการกำหนดให้กับวัตถุฐานนั้นเรียบร้อย แต่ฉันสามารถทำสิ่งเดียวกันใน Ruby โดยเปิดคลาสอีกครั้ง

ประการที่สี่: ความแตกต่างในทางปฏิบัติมีขนาดเล็กในขณะที่ปัญหาในทางปฏิบัติของการลืมที่จะใช้newมีขนาดใหญ่กว่ามาก - นั่นคือคุณมีแนวโน้มที่จะได้รับผลกระทบจากการขาดหายไปและnewคุณจะได้รับผลกระทบจากความแตกต่างระหว่างต้นแบบ .

ทั้งหมดที่กล่าวมาความแตกต่างระหว่างการทำต้นแบบและการสืบทอดแบบคลาสสิกคือสิ่งที่คุณเก็บไว้ (วิธีการ) นั้นเหมือนกับสิ่งที่คุณเก็บไว้ - ข้อมูล (อินสแตนซ์) ซึ่งหมายความว่าคุณสามารถสร้างชั้นเรียนได้ ทีละน้อยโดยใช้เครื่องมือการจัดการวัตถุเดียวกันทั้งหมดที่คุณจะใช้กับอินสแตนซ์ใด ๆ (อันที่จริงแล้วนี่คือวิธีที่ห้องสมุดจำลองคลาสทำทั้งหมดสำหรับวิธีการที่ไม่เหมือนคนอื่นดูที่Traits.js ) นี่เป็นเรื่องที่น่าสนใจเป็นอย่างยิ่งหากคุณกำลังใช้การเปรียบเทียบโปรแกรม


ใช่คำตอบที่ดีที่สุด IMO!
Aivar

0

Prototypal ที่สืบทอดใน JavaScript นั้นแตกต่างจากคลาสด้วยวิธีการสำคัญเหล่านี้:

คอนสตรัคเตอร์เป็นเพียงฟังก์ชั่นที่คุณสามารถโทรได้โดยไม่ต้องnew:

function Circle (r, x, y) { 
  //stuff here
}
Var c = new Circle();
Circle.call(c, x, y, z); //This works and you can do it over and over again.

ไม่มีตัวแปรหรือวิธีส่วนตัวที่ดีที่สุดที่คุณสามารถทำได้:

function Circle (r, x, y) {
  var color = 'red';
  function drawCircle () {
    //some code here with x, y and r
  }
  drawCircle();
  this.setX = function (x_) {
    x = x_;
    drawCircle();
  }

}
Circle.prototype.getX = function () {
   //Can't access x!
}

ในตัวอย่างก่อนหน้านี้คุณไม่สามารถขยายคลาสได้อย่างมีความหมายหากคุณใช้ตัวแปรและวิธีการส่วนตัวปลอมและนอกจากนี้วิธีสาธารณะใด ๆ ที่คุณประกาศจะถูกสร้างขึ้นใหม่ทุกครั้งที่มีการสร้างอินสแตนซ์ใหม่


คุณสามารถขยาย "คลาส" อย่างมีความหมาย คุณก็ไม่สามารถเข้าถึงตัวแปรท้องถิ่นcolor, r, x, yและdrawCircleที่ถูกผูกไว้กับขอบเขตของศัพท์Circle
Raynos

มันเป็นความจริงคุณสามารถทำได้ แต่คุณไม่สามารถทำได้โดยไม่ต้องสร้างวิธีการ 'สาธารณะ' หลายอย่างที่เพิ่มลงในบริบทที่สร้างขึ้นทุกครั้งที่คุณสร้างอินสแตนซ์
Bjorn

นี่เป็นรูปแบบที่แปลกมากที่คุณใช้อยู่ที่นั่น Circle.call()เป็นตัวสร้างหรือไม่? ดูเหมือนว่าคุณกำลังพยายามอธิบาย "วัตถุที่ใช้งานได้" (ผู้เรียกชื่อผิด ... )
Sean McMillan

มันไม่ใช่สิ่งที่ฉันเคยทำฉันแค่บอกว่าเป็นไปได้ที่จะเรียกนวกรรมิกซ้ำแล้วซ้ำอีกด้วย JavaScript แต่ไม่ใช่ในภาษาที่มีคลาสดั้งเดิม
Bjorn

1
แต่ความแตกต่างเหล่านี้เป็น 'สำคัญ' อย่างไร ฉันไม่เห็นการสร้างวัตถุโดยการเรียกใช้ฟังก์ชั่นไม่ใช่ใช้ 'ใหม่' เป็น 'สำคัญ' เป็นเพียงความแตกต่างของไวยากรณ์เล็กน้อย ในทำนองเดียวกันการไม่มีตัวแปรส่วนตัวไม่แตกต่างกันโดยพื้นฐานแตกต่างกันเล็กน้อย นอกจากนี้คุณยังสามารถมีตัวแปรส่วนตัว (คล้ายกับ) ในวิธีที่คุณอธิบาย
Roel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.