การอ้างอิงแบบวงกลมไปยังตัวชี้พาเรนต์ยอมรับได้เมื่อใด


24

คำถามStack Overflowนี้เกี่ยวกับชายด์ที่มีการอ้างอิงถึงพาเรนต์ผ่านตัวชี้

ความเห็นค่อนข้างสำคัญในตอนแรกของการออกแบบซึ่งเป็นความคิดที่น่ากลัว

ฉันเข้าใจว่านี่อาจไม่ใช่ความคิดที่ดีที่สุดโดยทั่วไป จากกฎทั่วไปของหัวแม่มือดูเหมือนว่ายุติธรรมที่จะพูดว่า "อย่าทำอย่างนี้!"

อย่างไรก็ตามฉันสงสัยว่ามีเงื่อนไขอะไรบ้างที่คุณจะต้องทำอะไรแบบนี้ คำถามนี้ที่นี่และคำตอบ / ความเห็นที่เกี่ยวข้องแนะนำแม้กระทั่งกราฟที่จะไม่ทำสิ่งนี้


1
คำถามที่คุณเชื่อมโยงนั้นค่อนข้างครอบคลุมในเรื่องนั้น ๆ
การแข่งขัน Lightness กับโมนิก้า

4
@LightnessRacesinOrbit "ไม่ทำเช่นนี้" ไม่ได้เป็นประโยชน์จริงๆเท่าที่การทำความเข้าใจว่าทำไม
enderland

2
ฉันเห็นมากกว่า "อย่าทำแบบนี้" ฉันเห็นข้อดีข้อเสียของผู้เชี่ยวชาญหลายคน
การแข่งขัน Lightness กับโมนิก้า

1
คุณอาจมีรายชื่อสองทางที่ต้องการการข้ามผ่านบัฟเฟอร์แบบวงกลมบางชนิดบางทีคุณอาจเป็นตัวแทนของถนนที่เชื่อมต่อกันสองชิ้นในเกม - ถ้าคุณต้องการเป็นตัวแทนของสิ่งที่เป็นวงกลมนี่อาจเป็นความคิดที่ดี
rhughes

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

คำตอบ:


43

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

วัตถุสองรายการไม่สามารถ "เป็นเจ้าของ" ซึ่งกันและกัน: สิ่งนี้ทำให้เกิดภาวะที่กลืนไม่เข้าคายไม่ออกยากสำหรับการเริ่มต้นและการลบคำสั่ง หนึ่งจะต้องมีการอ้างอิงตัวเลือกหรืออย่างอื่นระบุว่าวัตถุหนึ่งจะไม่จัดการอายุการใช้งานของอีก

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

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

ในการออกแบบ OO ส่วนใหญ่การอ้างอิงไปยังวัตถุอื่นในฐานะสมาชิกข้อมูลของวัตถุหมายถึงความเป็นเจ้าของ ตัวอย่างเช่นสมมติว่าเรามีคลาสรถยนต์และเครื่องยนต์ ไม่มีประโยชน์อย่างใดอย่างหนึ่งในตัวมันเอง เราสามารถพูดได้ว่าวัตถุเหล่านี้ขึ้นอยู่กับแต่ละอื่น ๆ พวกเขาต้องการการปรากฏตัวของอื่น ๆ เพื่อที่จะทำงานที่มีประโยชน์ แต่ "เจ้าของ" คนอื่น ๆ ? ในกรณีนี้เราจะบอกว่ารถยนต์เป็นเจ้าของเครื่องยนต์เพราะรถยนต์เป็น "ภาชนะบรรจุ" ที่ชิ้นส่วนยานยนต์ทั้งหมดมีชีวิตอยู่ ทั้งใน OO และการออกแบบในโลกแห่งความเป็นจริงรถเป็นผลรวมของชิ้นส่วนและชิ้นส่วนเหล่านั้นเชื่อมต่อกันภายในบริบทของรถ เครื่องยนต์อาจมีการอ้างอิงกลับไปที่รถยนต์หรืออาจมีการอ้างอิงถึง TorqueConverter

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

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


16

มีหลายด้านที่ต้องพิจารณาในการออกแบบเช่น:

  • การพึ่งพาโครงสร้าง
  • ความสัมพันธ์ของความเป็นเจ้าของ (iecomposition vs. การเชื่อมโยงอื่น ๆ )
  • การนำทางที่ต้องการ

การพึ่งพาโครงสร้างระหว่างคลาส:

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

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

กรรมสิทธิ์ในวัตถุ:

วัตถุหนึ่งเป็นเจ้าของวัตถุอื่นหรือไม่ หรือระบุไว้เป็นอย่างอื่น: หากวัตถุหนึ่งถูกทำลายวัตถุอื่นจะถูกทำลายเช่นกัน?

หัวข้อนี้ได้รับการกล่าวถึงอย่างลึกซึ้งโดย Snowman ดังนั้นฉันจะไม่พูดเรื่องนี้

ความต้องการการนำทางระหว่างวัตถุ:

ปัญหาล่าสุดคือความต้องการการนำทาง ลองมาตัวอย่างที่ชื่นชอบการออกแบบรูปแบบคอมโพสิตของแก๊งสี่

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

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

แน่นอนว่า Gamma & al ชี้ให้เห็นว่าคุณต้องรับรองความถูกต้องของความสัมพันธ์แบบวงกลม นี่อาจเป็นเรื่องยุ่งยากเนื่องจากคำถาม SO ที่คุณอ้างถึงได้แสดงไว้ แต่มันจัดการได้อย่างสมบูรณ์แบบและปลอดภัย

ข้อสรุป

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

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


1
คำตอบนี้ถูกยกเลิกอย่างมาก IMHO สหกรณ์ถามว่า: "ให้มีอยู่แล้วความสัมพันธ์แม่ลูกเมื่อมันตกลงที่จะดำเนินการนี้โดยการอ้างอิงแบบวงกลม" ดังนั้นโครงสร้างและ "ความเป็นเจ้าของ" (ในแง่ที่กล่าวถึงที่นี่) มีความชัดเจนอยู่แล้ว นั่นหมายถึงเฉพาะเกณฑ์สำหรับการเพิ่มการอ้างอิงในด้านใดด้านหนึ่งหรือเป็นคำถามของ "ความต้องการการนำทาง" และ "reusage อิสระของเด็ก"
Doc Brown

@DocBrown - คุณสามารถเติมเงินให้กับคำถามนี้ได้เสมอเมื่อมีสิทธิ์ :-)

1
@ GlenH7: นั่นจะไม่ให้คะแนนคำตอบนี้มีเพียงคำตอบของ Snowman เท่านั้น (ซึ่งฉันคิดว่ามันพลาดประเด็นไปซักนิด)
Doc Brown

1
@DocBrown - แต่ repz คน repz!

... และถ้าคุณเพิ่มตัวเลือกการเปิดเผยเช่นการป้องกันแบบสาธารณะเป็นการส่วนตัวซึ่งจะช่วยให้คุณมี 9 ตัวเลือกที่แตกต่างกัน :)
mikalai

8

โดยปกติแล้วการอ้างอิงแบบวงกลมเป็นแนวคิดที่ไม่ดีมากเพราะมันหมายถึงการอ้างอิงแบบวงกลม คุณอาจรู้อยู่แล้วว่าทำไมการอ้างอิงแบบวงกลมไม่ดี แต่เพื่อความสมบูรณ์ tl; dr version คือเมื่อใดก็ตามที่คลาส A และ B ทั้งสองขึ้นอยู่กับแต่ละอื่น ๆ มันเป็นไปไม่ได้ที่จะเข้าใจ / แก้ไข / เพิ่มประสิทธิภาพ / etc A หรือ B ทำความเข้าใจ / แก้ไข / เพิ่มประสิทธิภาพ / ฯลฯ คลาสอื่น ๆ ในเวลาเดียวกัน ซึ่งนำไปสู่โค้ดฐานที่คุณไม่สามารถเปลี่ยนแปลงอะไรได้อย่างรวดเร็วโดยไม่ต้องเปลี่ยนทุกอย่าง

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


6

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

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


5

[... ] แนะนำแม้กระทั่งกราฟที่จะไม่ทำสิ่งนี้

บางครั้งคุณจำเป็นต้องเข้าถึงสิ่งต่าง ๆ ในแบบล่างขึ้นบนจากโครงสร้างข้อมูลที่แตกต่างจากโครงสร้างต้นไม้ในขณะที่ต้นไม้จำเป็นต้องเข้าถึงสิ่งต่าง ๆ ในลักษณะจากบนลงล่าง

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


1
ใช่ตัวอย่างนี้เหมาะสมจริงๆ ชนิดของสิ่งที่ฉันพยายามอธิบายในอาร์กิวเมนต์ของฉันเกี่ยวกับการนำทางในคอมโพสิต: backpointer เร่งความเร็วอย่างมาก ขอบคุณสำหรับเกร็ดเล็กเกร็ดน้อยเกี่ยวกับประสิทธิภาพที่น่าสนใจและแผนภาพที่ชัดเจนมาก! +1
Christophe

@ Christophe ฉันชอบตัวอย่างของคุณเช่นกัน! ฉันไม่แน่ใจว่าจริง ๆ แล้วฉันตอบคำถามถูกต้องหรือไม่เพราะบางทีมันอาจเป็นเรื่องเกี่ยวกับ "ความเป็นเจ้าของแบบวงกลม" มากกว่าแค่พอยน์เตอร์พอยน์เตอร์ที่ให้เราสำรวจโครงสร้างข้อมูลขึ้น / ลง แต่ฉันส่วนใหญ่ตอบกลับไปที่ "กราฟ" ส่วนสุดท้ายของคำถาม

2

Doom 3 มีตัวอย่างของวัตถุลูกที่มีตัวชี้ไปยังวัตถุแม่ โดยเฉพาะจะใช้รายการล่วงล้ำ เพื่อสรุปรายการล่วงล้ำเป็นเหมือนรายการที่เชื่อมโยงยกเว้นแต่ละโหนดมีตัวชี้ไปยังรายการตัวเอง

ข้อดี:

  • เมื่อวัตถุสามารถมีอยู่ในหลายรายการพร้อมกันหน่วยความจำสำหรับโหนดรายการจะต้องได้รับการจัดสรรและจัดสรรคืนเพียงครั้งเดียว

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

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

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