Saya terlibat dalam diskusi pemrograman hari ini di mana saya membuat beberapa pernyataan yang pada dasarnya mengasumsikan secara aksiomatis bahwa referensi melingkar (antara modul, kelas, apa pun) umumnya buruk. Begitu saya selesai dengan nada saya, rekan kerja saya bertanya, "apa yang salah dengan referensi melingkar?"
Saya memiliki perasaan yang kuat tentang hal ini, tetapi sulit bagi saya untuk mengucapkan secara singkat dan konkret. Penjelasan apa pun yang mungkin saya buat cenderung bergantung pada item lain yang saya anggap juga aksioma ("tidak dapat digunakan dalam isolasi, jadi tidak dapat menguji", "perilaku tidak diketahui / tidak terdefinisi saat keadaan bermutasi pada objek yang berpartisipasi", dll. .), tetapi saya ingin mendengar alasan singkat mengapa referensi melingkar itu buruk yang tidak mengambil lompatan keyakinan seperti yang dimiliki otak saya sendiri, setelah menghabiskan berjam-jam selama bertahun-tahun menguraikannya untuk memahami, memperbaiki, dan memperluas berbagai bit kode.
Sunting: Saya tidak bertanya tentang referensi melingkar yang homogen, seperti yang ada dalam daftar yang tertaut ganda atau pointer-to-parent. Pertanyaan ini benar-benar bertanya tentang referensi melingkar "lingkup yang lebih besar", seperti libA calling libB yang memanggil kembali ke libA. Ganti 'modul' untuk 'lib' jika Anda mau. Terima kasih atas semua jawaban sejauh ini!
sumber
Jawaban:
Ada banyak hal yang salah dengan referensi melingkar:
Referensi kelas melingkar membuat kopling tinggi ; kedua kelas harus dikompilasi ulang setiap kali salah satu dari mereka diubah.
Edaran perakitan referensi mencegah statis menghubungkan , karena B tergantung pada A, tetapi A tidak dapat dirakit sampai B selesai.
Referensi objek melingkar dapat merusak algoritme rekursif naif (seperti serialisator, pengunjung, dan printer cantik) dengan stack overflows. Algoritma yang lebih maju akan memiliki deteksi siklus dan hanya akan gagal dengan pesan pengecualian / kesalahan yang lebih deskriptif.
Referensi objek melingkar juga membuat injeksi ketergantungan menjadi tidak mungkin , secara signifikan mengurangi testabilitas sistem Anda.
Objek dengan jumlah referensi melingkar yang sangat besar seringkali merupakan Objek Dewa . Bahkan jika tidak, mereka memiliki kecenderungan untuk mengarah pada Kode Spaghetti .
Edaran entitas referensi (terutama di database, tetapi juga dalam model domain) mencegah penggunaan kendala non-nullability , yang akhirnya dapat menyebabkan korupsi data atau setidaknya inkonsistensi.
Referensi lingkaran pada umumnya hanya membingungkan dan secara drastis meningkatkan beban kognitif ketika mencoba memahami bagaimana suatu program berfungsi.
Tolong, pikirkan anak-anak; hindari referensi melingkar kapan pun Anda bisa.
sumber
Referensi melingkar dua kali lipat dari referensi non-melingkar.
Jika Foo tahu tentang Bar, dan Bar tahu tentang Foo, Anda memiliki dua hal yang perlu diubah (ketika persyaratan datang, Foos dan Bar tidak boleh lagi saling mengenal). Jika Foo tahu tentang Bar, tetapi Bar tidak tahu tentang Foo, Anda dapat mengubah Foo tanpa menyentuh Bar.
Referensi siklis juga dapat menyebabkan masalah bootstrap, setidaknya di lingkungan yang bertahan lama (layanan dikerahkan, lingkungan pengembangan berbasis gambar), di mana Foo bergantung pada Bar yang bekerja untuk memuat, tetapi Bar juga tergantung pada Foo yang bekerja untuk beban.
sumber
Saat Anda menyatukan dua bit kode, Anda secara efektif memiliki satu kode besar. Kesulitan mempertahankan sedikit kode setidaknya kuadrat dari ukurannya, dan mungkin lebih tinggi.
Orang sering melihat kompleksitas kelas tunggal (/ fungsi / file / dll.) Dan lupa bahwa Anda benar-benar harus mempertimbangkan kompleksitas unit terkecil yang dapat dipisahkan (dienkapsulasi). Memiliki ketergantungan melingkar meningkatkan ukuran unit itu, mungkin tidak terlihat (sampai Anda mulai mencoba mengubah file 1 dan menyadari bahwa itu juga memerlukan perubahan pada file 2-127).
sumber
Mereka mungkin buruk bukan dengan sendirinya tetapi sebagai indikator kemungkinan desain yang buruk. Jika Foo bergantung pada Bar dan Bar bergantung pada Foo, maka dibenarkan untuk mempertanyakan mengapa mereka dua dan bukan FooBar yang unik.
sumber
Hmm ... itu tergantung apa yang Anda maksud dengan ketergantungan sirkular, karena sebenarnya ada beberapa dependensi sirkular yang menurut saya sangat bermanfaat.
Pertimbangkan XML DOM - masuk akal bagi setiap simpul untuk memiliki referensi ke orang tua mereka, dan bagi setiap orang tua untuk memiliki daftar anak-anaknya. Struktur secara logis adalah pohon, tetapi dari sudut pandang algoritma pengumpulan sampah atau sejenisnya strukturnya berbentuk lingkaran.
sumber
Node
adalah kelas, yang memiliki referensi lainNode
untuk anak-anak di dalamnya. Karena itu hanya merujuk sendiri, kelas sepenuhnya mandiri dan tidak digabungkan dengan yang lain. --- Dengan argumen ini, Anda dapat berargumen bahwa fungsi rekursif adalah ketergantungan sirkuler. Ini adalah (di sebuah peregangan), tapi tidak dalam cara yang buruk.Seperti masalah ayam atau telur .
Ada banyak kasus di mana referensi melingkar tidak dapat dihindari dan berguna tetapi, misalnya, dalam kasus berikut ini tidak berfungsi:
Proyek A tergantung pada proyek B dan B tergantung pada A. A perlu dikompilasi untuk digunakan dalam B yang mengharuskan B dikompilasi sebelum A yang mengharuskan B dikompilasi sebelum A yang ...
sumber
Sementara saya setuju dengan sebagian besar komentar di sini saya ingin memohon kasus khusus untuk referensi melingkar "orang tua" / "anak".
Kelas sering kali perlu mengetahui sesuatu tentang induknya atau kelas pemiliknya, mungkin perilaku default, nama file asal data, pernyataan sql yang memilih kolom, atau, lokasi file log, dll.
Anda dapat melakukan ini tanpa referensi melingkar dengan memiliki kelas yang berisi sehingga apa yang sebelumnya "orangtua" sekarang adalah saudara kandung, tetapi tidak selalu mungkin untuk faktor ulang kode yang ada untuk melakukan ini.
Alternatif lain adalah dengan memberikan semua data yang mungkin dibutuhkan seorang anak dalam konstruktornya, yang akhirnya menjadi sangat mengerikan.
sumber
Dalam istilah basis data, referensi melingkar dengan hubungan PK / FK yang tepat memungkinkan untuk memasukkan atau menghapus data. Jika Anda tidak dapat menghapus dari tabel a kecuali catatan hilang dari tabel b dan Anda tidak dapat menghapus dari tabel b kecuali catatan hilang dari tabel A, Anda tidak dapat menghapus. Sama dengan sisipan. inilah mengapa banyak basis data tidak memungkinkan Anda untuk mengatur pembaruan berjenjang atau menghapus jika ada referensi melingkar karena pada beberapa titik, itu menjadi tidak mungkin. Ya, Anda dapat mengatur hubungan semacam ini tanpa PK / Fk dinyatakan secara resmi tetapi kemudian Anda akan (100% dari waktu dalam pengalaman saya) memiliki masalah integritas data. Itu hanya desain yang buruk.
sumber
Saya akan mengambil pertanyaan ini dari sudut pandang pemodelan.
Selama Anda tidak menambahkan hubungan yang sebenarnya tidak ada, Anda aman. Jika Anda menambahkannya, Anda mendapatkan integritas data yang lebih sedikit (karena ada redundansi) dan kode yang lebih erat.
Masalahnya dengan referensi melingkar secara khusus adalah bahwa saya belum melihat kasus di mana mereka benar-benar diperlukan kecuali referensi satu-diri. Jika Anda membuat model pohon atau grafik, Anda memerlukannya dan itu benar-benar baik-baik saja karena referensi-diri tidak berbahaya dari sudut pandang kualitas kode (tidak ada ketergantungan ditambahkan).
Saya percaya bahwa pada saat Anda mulai memerlukan referensi bukan diri, segera Anda harus bertanya apakah Anda tidak dapat memodelkannya sebagai grafik (pisahkan beberapa entitas menjadi satu - simpul). Mungkin ada kasus di antara di mana Anda membuat referensi melingkar tetapi memodelkannya sebagai grafik tidak sesuai tetapi saya sangat meragukannya.
Ada bahaya bahwa orang berpikir bahwa mereka membutuhkan referensi melingkar tetapi kenyataannya tidak. Kasus yang paling umum adalah "Kasus satu-dari-banyak". Misalnya, Anda telah mendapatkan pelanggan dengan beberapa alamat dari mana satu harus ditandai sebagai alamat utama. Sangat menggoda untuk memodelkan situasi ini karena dua hubungan terpisah has_address dan is_primary_address_of tetapi tidak benar. Alasannya adalah bahwa menjadi alamat utama bukan hubungan terpisah antara pengguna dan alamat, tetapi sebaliknya itu adalah atribut dari hubungan yang memiliki alamat. Mengapa demikian? Karena domainnya terbatas pada alamat pengguna dan tidak untuk semua alamat yang ada. Anda memilih salah satu tautan dan menandainya sebagai yang terkuat (utama).
(Pergi untuk berbicara tentang database sekarang) Banyak orang memilih untuk solusi dua-hubungan karena mereka memahami "primer" sebagai penunjuk unik dan kunci asing adalah semacam penunjuk. Jadi kunci asing harus digunakan, bukan? Salah. Kunci asing mewakili hubungan tetapi "primer" bukan hubungan. Ini adalah kasus degenerasi dari pemesanan di mana satu elemen di atas semua dan sisanya tidak dipesan. Jika Anda perlu memodelkan total pemesanan, Anda tentu akan menganggapnya sebagai atribut hubungan karena pada dasarnya tidak ada pilihan lain. Tetapi pada saat Anda merosotkannya, ada pilihan dan cukup mengerikan - untuk membuat model sesuatu yang bukan hubungan sebagai suatu hubungan. Jadi ini dia - hubungan redundansi yang tentu saja bukan sesuatu yang bisa diremehkan.
Jadi, saya tidak akan membiarkan referensi melingkar terjadi kecuali benar-benar jelas bahwa itu berasal dari hal yang saya modelkan.
(catatan: ini sedikit bias pada desain basis data, tetapi saya berani bertaruh itu juga berlaku untuk area lain)
sumber
Saya akan menjawab pertanyaan itu dengan pertanyaan lain:
Situasi apa yang dapat Anda berikan kepada saya di mana menjaga model referensi melingkar adalah model terbaik untuk apa yang Anda coba bangun?
Dari pengalaman saya, model terbaik tidak akan pernah melibatkan referensi melingkar dalam cara saya pikir Anda bersungguh-sungguh. Yang sedang berkata, ada banyak model di mana Anda menggunakan referensi melingkar sepanjang waktu, itu hanya sangat mendasar. Induk -> Hubungan anak-anak, model grafik apa saja, dll, tetapi ini adalah model-model terkenal dan saya pikir Anda mengacu pada sesuatu yang lain sama sekali.
sumber
Referensi melingkar dalam struktur data terkadang merupakan cara alami untuk mengekspresikan model data. Dari sisi pengkodean, ini jelas tidak ideal dan dapat (sampai batas tertentu) diselesaikan dengan injeksi ketergantungan, mendorong masalah dari kode ke data.
sumber
Konstruk referensi melingkar bermasalah, tidak hanya dari sudut pandang desain, tetapi dari sudut pandang penangkap kesalahan juga.
Pertimbangkan kemungkinan kegagalan kode. Anda belum menempatkan kesalahan yang tepat di kedua kelas, baik karena Anda belum mengembangkan metode Anda sejauh itu, atau Anda malas. Either way, Anda tidak memiliki pesan kesalahan untuk memberi tahu Anda apa yang terjadi, dan Anda perlu men-debug itu. Sebagai perancang program yang baik, Anda tahu metode apa yang terkait dengan proses apa, sehingga Anda dapat mempersempitnya ke metode yang relevan dengan proses yang menyebabkan kesalahan.
Dengan referensi melingkar, masalah Anda sekarang menjadi dua kali lipat. Karena proses Anda terikat erat, Anda tidak memiliki cara untuk mengetahui metode mana di kelas mana yang mungkin menyebabkan kesalahan, atau dari mana kesalahan itu terjadi, karena satu kelas tergantung pada yang lain tergantung pada yang lain. Anda sekarang harus menghabiskan waktu menguji kedua kelas dalam hubungannya untuk mencari tahu mana yang benar-benar bertanggung jawab atas kesalahan tersebut.
Tentu saja, penangkapan kesalahan yang tepat menyelesaikan ini, tetapi hanya jika Anda tahu kapan kesalahan mungkin terjadi. Dan jika Anda menggunakan pesan kesalahan umum, Anda masih tidak jauh lebih baik.
sumber
Beberapa pengumpul sampah kesulitan membersihkannya, karena setiap objek dirujuk oleh yang lain.
EDIT: Seperti dicatat oleh komentar di bawah ini, ini hanya berlaku untuk upaya yang sangat naif pada pengumpul sampah, bukan yang pernah Anda temui dalam praktek.
sumber
Menurut pendapat saya memiliki referensi tidak terbatas membuat desain program lebih mudah, tetapi kita semua tahu bahwa beberapa bahasa pemrograman kurang mendukung mereka dalam beberapa konteks.
Anda menyebutkan referensi antar modul atau kelas. Dalam hal ini adalah hal yang statis, yang telah ditentukan oleh pemrogram, dan itu jelas mungkin bagi pemrogram untuk mencari struktur yang tidak memiliki bundar, meskipun mungkin tidak cocok dengan masalah dengan bersih.
Masalah sebenarnya datang dalam sirkularitas dalam struktur data run time, di mana beberapa masalah sebenarnya tidak dapat didefinisikan dengan cara yang menghilangkan sirkularitas. Pada akhirnya - masalah yang harus menentukan dan membutuhkan hal lain adalah memaksa programmer untuk memecahkan teka-teki yang tidak perlu.
Saya akan mengatakan itu masalah dengan alat bukan masalah dengan prinsip.
sumber