it-swarm-id.com

Apa yang salah dengan referensi sirkuler?

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.

Edit: 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!

167
dash-tom-bang

Ada banyak yang salah dengan referensi melingkar:

  • Referensi melingkar kelas membuat kopling tinggi ; keduanya kelas harus dikompilasi ulang setiap kali baik dari mereka diubah.

  • Circular Rakitan referensi mencegah penautan statis , karena B bergantung pada A tetapi A tidak dapat dirakit sampai B selesai.

  • Referensi melingkar objek dapat crash algoritma rekursif naif (seperti serialisator, pengunjung dan pretty-printer) dengan stack overflows. Algoritma yang lebih maju akan memiliki deteksi siklus dan hanya akan gagal dengan pesan pengecualian/kesalahan yang lebih deskriptif.

  • Edaran objek referensi juga membuat injeksi ketergantungan tidak mungkin , secara signifikan mengurangi testabilitas dari sistem Anda.

  • Objek dengan jumlah sangat besar besar referensi melingkar sering Obyek Dewa . Bahkan jika tidak, mereka memiliki kecenderungan untuk mengarah ke Kode Spaghetti .

  • Circular entitas referensi (terutama dalam database, tetapi juga dalam model domain) mencegah penggunaan batasan non-nullability , yang pada akhirnya dapat menyebabkan korupsi data atau setidaknya inkonsistensi.

  • Referensi melingkar secara umum hanyalah membingungkan dan secara drastis meningkatkan beban kognitif ketika berusaha memahami bagaimana suatu program berfungsi.

Tolong, pikirkan anak-anak; hindari referensi melingkar kapan pun Anda bisa.

228
Aaronaught

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 bergantung pada Foo yang bekerja untuk beban.

22
Frank Shearar

Ketika Anda mengikat dua bit kode bersama-sama, Anda secara efektif memiliki satu kode besar. Kesulitan mempertahankan sedikit kode setidaknya kuadrat dari ukurannya, dan mungkin lebih tinggi.

Orang-orang sering melihat kompleksitas kelas tunggal (/ function/file/etc.) 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).

17
Alex Feinman

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.

14
mouviciel

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 untuk setiap orang tua memiliki daftar anak-anaknya. Struktur secara logis adalah pohon, tetapi dari sudut pandang algoritma pengumpulan sampah atau sejenisnya strukturnya berbentuk lingkaran.

10
Billy ONeal

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 ...

9
Victor Hurdugaci

Sementara saya setuju dengan sebagian besar komentar di sini saya ingin memohon kasus khusus untuk referensi melingkar "orangtua"/"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.

6
James Anderson

Dalam istilah basis data, referensi melingkar dengan hubungan PK/FK yang tepat membuatnya tidak mungkin 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.

5
HLGEM

Saya akan mengambil pertanyaan ini dari sudut pandang pemodelan.

Selama Anda tidak menambahkan hubungan yang tidak benar-benar ada, Anda aman. Jika Anda menambahkannya, Anda mendapatkan kurang integritas data (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 sendiri 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 pada kenyataannya tidak. Kasus yang paling umum adalah "Kasus satu-dari-banyak". Misalnya, Anda telah mendapatkan pelanggan dengan beberapa alamat yang harus ditandai sebagai alamat utama. Sangat menggoda untuk memodelkan situasi ini sebagai dua hubungan yang terpisah has_address dan is_primary_address_of tetapi itu tidak benar. Alasannya adalah bahwa menjadi alamat utama bukan hubungan terpisah antara pengguna dan alamat tetapi sebaliknya atribut dari hubungan 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).

(Akan berbicara tentang basis data 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 merosotnya, ada pilihan dan cukup mengerikan - untuk membuat model sesuatu yang bukan hubungan sebagai suatu hubungan. Jadi begini - hubungan redundansi yang tentu saja bukan sesuatu yang harus diremehkan. Persyaratan keunikan harus diberlakukan dengan cara lain, misalnya dengan indeks parsial unik.

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)

4
clime

Saya akan menjawab pertanyaan itu dengan pertanyaan lain:

Situasi apa yang dapat Anda berikan kepada saya di mana menyimpan model referensi melingkar adalah model terbaik untuk apa yang Anda coba buat?

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.

2
Joseph

Beberapa pengumpul sampah kesulitan membersihkannya, karena setiap objek dirujuk oleh yang lain.

EDIT: Seperti dicatat oleh komentar di bawah ini, ini hanya berlaku untuk sangat upaya naif pada pengumpul sampah, bukan yang pernah Anda temui dalam praktek .

1
shmuelp

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 bisa 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 mana kelas 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 belum jauh lebih baik.

1
Zibbobz

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.

1
Vatine