ลักษณะในสกาล่าหลีกเลี่ยง "ข้อผิดพลาดของเพชร" ได้อย่างไร


16

(หมายเหตุ: ฉันใช้ 'ข้อผิดพลาด' แทน 'ปัญหา' ในชื่อด้วยเหตุผลที่ชัดเจน .. ;))

ฉันได้อ่านพื้นฐานเกี่ยวกับลักษณะนิสัยในสกาล่า มันคล้ายกับอินเตอร์เฟสใน Java หรือ C # แต่อนุญาตให้มีการใช้งานเมธอดที่เป็นค่าเริ่มต้น

ฉันสงสัยว่า: สิ่งนี้ไม่สามารถทำให้เกิดปัญหา "ปัญหาเพชร" ได้หรือไม่ซึ่งเป็นเหตุผลว่าทำไมหลาย ๆ ภาษาจึงหลีกเลี่ยงการสืบทอดหลายอย่างตั้งแต่แรก?

ถ้าเป็นเช่นนั้นสกาล่าจัดการเรื่องนี้อย่างไร?


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

2
@gnat: นี่เป็นคำถามเชิงแนวคิดไม่ใช่คำถามปัญหาที่เป็นรูปธรรม ถ้าเขาถามว่า "ฉันมีคลาสนี้ที่สกาล่าและมันทำให้ฉันมีปัญหาซึ่งฉันคิดว่าอาจเกี่ยวข้องกับปัญหาเพชรฉันจะแก้ไขได้อย่างไร" ความคิดเห็นของคุณจะเหมาะสม แต่แล้วคำถามก็จะอยู่ใน SO : P
Mason Wheeler

@MasonWheeler ฉันได้อ่านพื้นฐานเกี่ยวกับ Scala ด้วย และการค้นหาครั้งแรกสำหรับ "เพชร" ในสิ่งที่ฉันได้อ่านให้ฉันคำตอบ: "ลักษณะมีคุณสมบัติทั้งหมดของการสร้างส่วนต่อประสาน Java แต่ลักษณะสามารถมีวิธีการดำเนินการกับพวกเขาหากคุณคุ้นเคยกับทับทิมลักษณะที่คล้ายกัน ไปยังมิกซ์ของ Ruby คุณสามารถผสมหลายลักษณะเข้าไปในคลาสเดียวได้ลักษณะไม่สามารถใช้พารามิเตอร์คอนสตรัคเตอร์ได้ แต่นอกเหนือจากที่พวกมันทำงานเหมือนคลาสนี่ทำให้คุณมีความสามารถที่จะมีบางอย่าง การขาดความพยายามในคำถามนี้รู้สึกโจ่งแจ้งมาก
gnat

7
การอ่านคำแถลงนั้นไม่ได้บอกคุณว่ามันเป็นเช่นไร
Michael Brown

คำตอบ:


22

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

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

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

ถ้า B และ C สืบทอด A และ D รับ B และ C และทั้ง B และ C เรียก C ของคอนสตรัคเตอร์ A และ B ของ C จะเรียกคอนสตรัคเตอร์ของ A สองครั้ง การกำหนดว่าการใช้งานแบบใดให้เลือกเช่น Scala ที่ทำกับวิธีการจะไม่ทำงานที่นี่เพราะทั้งสองอย่าง B และ C จะต้องถูกเรียก

ลักษณะหลีกเลี่ยงปัญหานี้เนื่องจากไม่มีตัวสร้าง


1
เป็นไปได้ที่จะใช้การสร้างเชิงเส้นตรง C3เพื่อโทรหาคอนสตรัคเตอร์เพียงครั้งเดียว - นั่นคือวิธีที่ Python ทำการสืบทอดหลาย ๆ แบบ การจัดวางเชิงเส้นสำหรับ D <B | C <A เพชรคือ D -> B -> C -> A. ยิ่งกว่านั้นการค้นหาโดย Google ได้แสดงให้ฉันเห็นว่าลักษณะของสกาล่าสามารถมีตัวแปรที่ไม่แน่นอนดังนั้นจึงมี คอนสตรัคที่ไหนสักแห่งในนั้น? แต่ถ้ามันใช้การจัดวางใต้กระโปรง (ฉันไม่รู้ก็ไม่เคยใช้ Scala) มันไม่ยากเลยที่จะเห็นว่า B และ C สามารถแบ่งปันกับ A ...
Doval

... ลักษณะดูเหมือนเป็นวิธีที่รัดกุมมากในการถ่ายทอดแผ่นความร้อนทั้งหมดที่รวมการสืบทอดส่วนต่อประสานและการมอบหมาย + องค์ประกอบซึ่งเป็นวิธีที่ถูกต้องในการนำพฤติกรรมกลับมาใช้ใหม่
Doval

@Doval ประสบการณ์ของฉันของผู้สร้างใน Python ที่สืบทอดมาหลายต่อหลายครั้งคือพวกมันเป็นความเจ็บปวดของราชวงศ์ คอนสตรัคแต่ละตัวไม่สามารถรู้ได้ว่าจะเรียกมันตามลำดับใดดังนั้นไม่รู้ว่าอะไรคือลายเซ็นของคอนสตรัคเตอร์หลัก วิธีแก้ปัญหาปกติสำหรับคอนสตรัคเตอร์ทุกคนจะใช้อาร์กิวเมนต์ของคีย์เวิร์ดเป็นจำนวนมากและส่งค่าที่ไม่ได้ใช้ไปยังคอนสตรัคเตอร์สุดยอดของมัน แต่ถ้าคุณต้องการทำงานกับคลาสที่มีอยู่แล้ว มัน.
James_pic

คำถามอีกข้อหนึ่งก็คือทำไม C ++ ไม่ได้เลือกนโยบายที่มีเหตุผลสำหรับปัญหาเพชร
ผู้ใช้

20

สกาล่าหลีกเลี่ยงปัญหาเพชรโดยสิ่งที่เรียกว่า "ลักษณะเชิงเส้นเชิงเส้น" โดยทั่วไปจะค้นหาวิธีการนำไปใช้ในลักษณะที่คุณขยายจากขวาไปซ้าย ตัวอย่างง่ายๆ:

trait Base {
   def op: String
}

trait Foo extends Base {
   override def op = "foo"
}

trait Bar extends Base {
   override def op = "bar"
}

class A extends Foo with Bar
class B extends Bar with Foo

(new A).op
// res0: String = bar

(new B).op
// res1: String = foo

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

ฉันเชื่อในภาษาการเขียนโปรแกรมอื่น ๆ พฤติกรรมนี้บางครั้งเรียกว่า "ลำดับวิธีการแก้ไข" หรือ "MRO"

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