นี่เป็นอีกหนึ่งปัญหาในการออกแบบภาษาที่ดูเหมือนว่า "เป็นความคิดที่ดี" จนกว่าคุณจะเริ่มขุดคุ้ยและรู้ว่านั่นเป็นความคิดที่ไม่ดี
เมลนี้มีเนื้อหามากมายเกี่ยวกับเรื่องนี้ (และในเรื่องอื่น ๆ ด้วย) มีกองกำลังออกแบบหลายอย่างที่มาบรรจบกันเพื่อนำเราไปสู่การออกแบบปัจจุบัน:
- ความปรารถนาที่จะรักษารูปแบบการสืบทอดให้เรียบง่าย
- ความจริงที่ว่าเมื่อคุณดูตัวอย่างที่เห็นได้ชัด (เช่นเปลี่ยน
AbstractList
เป็นอินเทอร์เฟซ) คุณจะรู้ว่าการสืบทอด equals / hashCode / toString นั้นเชื่อมโยงอย่างมากกับการสืบทอดและสถานะเดียว
- มันอาจเปิดประตูไปสู่พฤติกรรมที่น่าประหลาดใจ
คุณได้สัมผัสกับเป้าหมาย "ทำให้มันง่าย" แล้ว กฎการสืบทอดและการแก้ปัญหาความขัดแย้งได้รับการออกแบบมาให้ง่ายมาก (คลาสชนะเหนืออินเทอร์เฟซอินเทอร์เฟซที่ได้รับจะชนะเหนืออินเทอร์เฟซที่ได้รับและข้อขัดแย้งอื่น ๆ จะได้รับการแก้ไขโดยคลาสที่ใช้งาน) แน่นอนว่ากฎเหล่านี้สามารถปรับแต่งเพื่อให้มีข้อยกเว้นได้ แต่ ฉันคิดว่าคุณจะพบเมื่อคุณเริ่มดึงสตริงนั้นความซับซ้อนที่เพิ่มขึ้นนั้นไม่ได้เล็กอย่างที่คุณคิด
แน่นอนว่ามีประโยชน์ระดับหนึ่งที่จะปรับความซับซ้อนให้มากขึ้น แต่ในกรณีนี้ไม่มี วิธีการที่เราพูดถึงที่นี่คือเท่ากับ hashCode และ toString วิธีการเหล่านี้ล้วนเกี่ยวกับสถานะออบเจ็กต์อย่างแท้จริงและเป็นคลาสที่เป็นเจ้าของสถานะไม่ใช่อินเทอร์เฟซซึ่งอยู่ในตำแหน่งที่ดีที่สุดในการพิจารณาว่าความเท่าเทียมกันหมายความว่าอย่างไรสำหรับคลาสนั้น (โดยเฉพาะอย่างยิ่งเนื่องจากสัญญาเพื่อความเท่าเทียมกันนั้นค่อนข้างแข็งแกร่งดูประสิทธิผล Java สำหรับผลลัพธ์ที่น่าประหลาดใจ); ผู้เขียนอินเทอร์เฟซถูกลบออกไปไกลเกินไป
ดึงAbstractList
ตัวอย่างออกมาได้ง่าย คงจะดีไม่น้อยหากเราสามารถกำจัดAbstractList
และนำพฤติกรรมดังกล่าวลงในList
อินเทอร์เฟซได้ แต่เมื่อคุณก้าวไปไกลกว่าตัวอย่างที่ชัดเจนนี้แล้วจะไม่มีตัวอย่างที่ดีอื่น ๆ อีกมากมายให้พบ ที่รากAbstractList
ถูกออกแบบมาสำหรับการสืบทอดครั้งเดียว แต่อินเทอร์เฟซต้องได้รับการออกแบบสำหรับการสืบทอดหลาย ๆ
นอกจากนี้ลองจินตนาการว่าคุณกำลังเขียนชั้นเรียนนี้:
class Foo implements com.libraryA.Bar, com.libraryB.Moo {
// Implementation of Foo, that does NOT override equals
}
Foo
เขียนรูปลักษณ์ที่ supertypes Object
ที่เห็นการดำเนินการเท่ากับไม่มีและสรุปว่าจะได้รับความเสมอภาคอ้างอิงทั้งหมดที่เขาต้องทำคือเท่ากับสืบทอดมาจาก จากนั้นในสัปดาห์หน้าผู้ดูแลห้องสมุดสำหรับ Bar "helpfully" จะเพิ่มการequals
ใช้งานเริ่มต้น อ๊ะ! ตอนนี้ความหมายของFoo
อินเทอร์เฟซในโดเมนการบำรุงรักษาอื่น "เป็นประโยชน์" ได้เพิ่มค่าเริ่มต้นสำหรับวิธีการทั่วไป
ค่าเริ่มต้นควรเป็นค่าเริ่มต้น การเพิ่มค่าดีฟอลต์ให้กับอินเทอร์เฟซที่ไม่มี (ที่ใดก็ได้ในลำดับชั้น) ไม่ควรส่งผลกระทบต่อความหมายของคลาสการใช้งานคอนกรีต แต่ถ้าค่าเริ่มต้นสามารถ "ลบล้าง" เมธอด Object ได้นั่นจะไม่เป็นความจริง
ดังนั้นแม้ว่าจะดูเหมือนเป็นคุณสมบัติที่ไม่เป็นอันตราย แต่ในความเป็นจริงมันก็ค่อนข้างอันตราย: มันเพิ่มความซับซ้อนมากมายสำหรับการแสดงออกที่เพิ่มขึ้นเล็กน้อยและมันทำให้ง่ายเกินไปสำหรับการเปลี่ยนแปลงที่มีเจตนาดีและไม่เป็นอันตรายต่ออินเทอร์เฟซที่คอมไพล์แยกต่างหากเพื่อบ่อนทำลาย ความหมายที่ตั้งใจไว้ของการใช้คลาส