Pertanyaan Stack Overflow ini adalah tentang seorang anak yang memiliki referensi ke induknya, melalui sebuah pointer.
Komentar awalnya cukup kritis dari desain menjadi ide yang mengerikan.
Saya mengerti ini mungkin bukan ide terbaik secara umum. Dari aturan umum tampaknya adil untuk mengatakan, "jangan lakukan ini!"
Namun, saya bertanya-tanya kondisi seperti apa yang akan ada di mana Anda perlu melakukan sesuatu seperti ini. Pertanyaan ini di sini dan jawaban / komentar terkait menyarankan bahkan untuk grafik untuk tidak melakukan hal seperti ini.
Jawaban:
Kuncinya di sini bukanlah apakah dua objek memiliki referensi melingkar, tetapi apakah referensi tersebut menunjukkan kepemilikan satu sama lain.
Dua objek tidak dapat "memiliki" satu sama lain: ini menyebabkan dilema yang sulit untuk inisialisasi dan urutan penghapusan. Satu harus menjadi referensi opsional, atau menunjukkan bahwa satu objek tidak akan mengatur masa pakai yang lain.
Pertimbangkan daftar yang ditautkan dua kali lipat: dua simpul saling bolak-balik, tetapi tidak ada yang "memiliki" yang lain (daftar itu memiliki keduanya). Ini berarti tidak ada node yang mengalokasikan memori untuk yang lain atau bertanggung jawab atas identitas atau manajemen seumur hidup yang lain.
Pohon memiliki hubungan yang serupa, meskipun simpul dalam pohon dapat mengalokasikan anak-anak dan orang tua memiliki anak sendiri. Tautan dari anak ke orang tua membantu dengan traversal, tetapi sekali lagi tidak mendefinisikan kepemilikan.
Dalam sebagian besar desain OO, referensi ke objek lain sebagai anggota data objek menyiratkan kepemilikan. Sebagai contoh, misalkan kita memiliki kelas Mobil dan Mesin. Tak satu pun dari mereka yang sangat berguna. Kita dapat mengatakan bahwa objek-objek ini saling bergantung : mereka membutuhkan kehadiran yang lain untuk melakukan pekerjaan yang bermanfaat. Tetapi yang "memiliki" yang lain? Dalam hal ini kita akan mengatakan bahwa Mobil memiliki Mesin karena mobil adalah "wadah" di mana semua komponen otomotif hidup. Baik dalam desain OO dan dunia nyata, mobil adalah jumlah dari bagian-bagiannya, dan semua bagian itu terhubung bersama dalam konteks mobil. Engine mungkin memiliki referensi kembali ke Mobil, atau mungkin memiliki referensi ke TorqueConverter,
Referensi melingkar bisa menjadi bau desain yang buruk, tetapi tidak harus. Ketika digunakan secara bijaksana dan didokumentasikan dengan benar, mereka dapat membuat penggunaan struktur data lebih mudah.
Coba lewati sebatang pohon tanpa referensi dari kedua orang tua dan anak-anak. Tentu, Anda bisa membuat pendekatan berbasis tumpukan yang rapuh dan kompleks, atau Anda bisa menggunakan pendekatan berbasis referensi yang sederhana.
sumber
Ada beberapa aspek yang perlu dipertimbangkan dalam desain seperti:
Ketergantungan struktural antar kelas:
Jika Anda bertujuan menggunakan kembali kelas komponen, Anda harus menghindari ketergantungan yang tidak perlu dan menghindari struktur melingkar yang tertutup.
Namun demikian kadang-kadang dua kelas secara konsep sangat terkait. Dalam hal ini, menghindari ketergantungan bukanlah pilihan nyata. Contoh: pohon dan daunnya, atau lebih umum komposit dan komponennya .
Kepemilikan benda:
Apakah satu objek memiliki yang lain? Atau dinyatakan lain: jika satu objek dihancurkan, apakah yang lain akan dihancurkan juga?
Topik ini dibahas secara mendalam oleh Snowman, jadi saya tidak akan membahasnya di sini.
Kebutuhan navigasi antara objek:
Masalah terakhir adalah kebutuhan navigasi. Mari kita ambil contoh favorit saya, pola desain komposit dari Gang empat .
Gamma & al. secara eksplisit menyebutkan kebutuhan potensial untuk memiliki referensi orang tua yang eksplisit: " Mempertahankan referensi dari komponen anak ke orang tua mereka dapat menyederhanakan traversal dan pengelolaan struktur komposit " Tentu saja Anda dapat membayangkan traversal top-down yang sistematis, tetapi untuk objek komposit yang sangat besar itu dapat secara signifikan memperlambat operasi dan secara eksponensial. Referensi langsung, bahkan lingkaran dapat secara signifikan memudahkan manipulasi komposit Anda.
Contohnya bisa menjadi model grafis dari sistem elektronik. Struktur komposit dapat mewakili papan elektronik, sirkuit, elemen. Untuk menampilkan dan memanipulasi model, Anda memerlukan beberapa proksi geometris dalam tampilan GUI. Maka tentu saja jauh lebih mudah untuk menavigasi dari elemen GUI yang dipilih oleh pengguna ke komponen, untuk mengetahui yang merupakan induk dan dengan elemen saudara / saudari terkait, daripada memulai pencarian top-down.
Tentu saja, seperti yang ditunjukkan oleh Gamma & al, Anda harus memastikan invarian dari hubungan sirkuler. Ini bisa rumit, seperti yang ditunjukkan oleh pertanyaan SO yang Anda lihat. Tapi itu sangat mudah dikelola dan dengan cara yang aman.
Kesimpulan
Kebutuhan navigasi tidak boleh diremehkan. Bukan tanpa alasan bahwa UML telah secara eksplisit menekannya dalam notasi pemodelan. Dan ya, ada situasi yang benar-benar valid di mana referensi melingkar diperlukan.
Satu-satunya titik adalah bahwa kadang-kadang orang cenderung pergi ke arah yang cepat. Jadi ada baiknya mempertimbangkan semua 3 aspek yang terlibat sebelum mengambil keputusan untuk melakukannya atau tidak.
sumber
Biasanya, referensi melingkar adalah ide yang sangat buruk karena artinya ketergantungan sirkular. Anda mungkin sudah tahu mengapa edaran melingkar buruk, tetapi demi kelengkapan, versi dr adalah bahwa setiap kali kelas A dan B keduanya saling bergantung, tidak mungkin untuk memahami / memperbaiki / mengoptimalkan / dll, baik A atau B tanpa A juga B memahami / memperbaiki / mengoptimalkan / dll kelas lain secara bersamaan. Yang dengan cepat mengarah ke basis kode di mana Anda tidak dapat mengubah apa pun tanpa mengubah segalanya.
Namun, adalah mungkin untuk memiliki referensi melingkar tanpa menciptakan kejahatan dependensi melingkar. Ini berfungsi selama referensi secara ketat opsional dalam arti fungsional. Maksud saya, Anda dapat dengan mudah menghapusnya dari kelas dan mereka akan tetap bekerja, bahkan jika mereka bekerja lebih lambat. Kasus penggunaan utama yang saya ketahui untuk referensi non-dependensi yang melingkar seperti ini memungkinkan lintasan cepat struktur data berbasis simpul seperti daftar tertaut, pohon, dan tumpukan. Misalnya, pada prinsipnya operasi apa pun yang dapat Anda lakukan pada daftar yang ditautkan dua kali lipat, Anda juga dapat melakukan pada daftar yang terhubung sendiri, kebetulan ada beberapa operasi (seperti bergerak mundur melalui daftar) yang memiliki besar lebih baik O dengan versi yang ditautkan dua kali lipat.
sumber
Alasan biasanya bukan ide yang baik untuk melakukan ini adalah karena itu melanggar Prinsip Ketergantungan Inversi . Orang-orang telah menulis banyak tentang hal ini secara lebih terperinci daripada yang dapat saya bahas secara memadai dalam posting ini, tetapi hal ini membuat sulit untuk mempertahankannya, karena sambungannya sangat rapat. Mengubah salah satu kelas hampir selalu mengharuskan adanya perubahan di yang lain, sedangkan jika dependensi hanya menunjuk satu arah, perubahan di satu sisi antarmuka terisolasi. Jika kedua kelas menunjuk ke antarmuka abstrak, bahkan lebih baik.
Satu pengecualian utama adalah ketika Anda tidak memiliki dua kelas yang berbeda pada tingkat abstraksi yang berbeda, tetapi dua simpul dari kelas yang sama, seperti di pohon, daftar yang ditautkan dua kali lipat, dll. Ini lebih merupakan hubungan struktural daripada hubungan abstraksi. Referensi melingkar demi efisiensi algoritmik dapat diterima dan bahkan didorong dalam kasus-kasus seperti ini.
sumber
Terkadang Anda hanya perlu mengakses hal-hal secara bottom-up dari struktur data yang berbeda dari pohon, sedangkan pohon perlu mengakses hal-hal dengan cara top-down.
Sebagai contoh, quadtree mungkin menyimpan elemen dalam perangkat lunak grafik vektor. Namun, pilihan pengguna disimpan dalam daftar pilihan referensi elemen vektor yang terpisah. Ketika pengguna ingin menghapus pilihan itu, kami harus memperbarui quadtree, dan mungkin ada jauh lebih efisien untuk memperbarui pohon dengan cara bottom-up mulai dari daun daripada top-down. Kalau tidak, Anda harus, untuk setiap elemen, bekerja dari akar ke daun dan kemudian kembali lagi.
sumber
Doom 3 memiliki contoh objek anak dengan pointer ke objek induk. Secara khusus ia menggunakan daftar yang mengganggu . Untuk meringkas, daftar intrusi seperti daftar tertaut kecuali setiap node berisi pointer ke daftar itu sendiri.
Keuntungan:
Ketika objek dapat ada di beberapa daftar secara bersamaan, memori untuk daftar node perlu dialokasikan dan dideallokasi hanya sekali.
Ketika sebuah objek perlu dihancurkan, Anda dapat dengan mudah menghapusnya dari semua daftar yang ada di dalamnya tanpa mencari melalui setiap daftar secara linear.
Saya pikir ini adalah skenario yang cukup spesifik, tetapi jika saya memahami pertanyaan Anda, ini adalah contoh penggunaan objek anak yang dapat diterima yang berisi pointer ke objek induknya.
sumber