Üst işaretçi için dairesel bir başvuru ne zaman kabul edilebilir?


24

Bu Yığın Taşması sorusu, bir işaretçi aracılığıyla ebeveyni referansı olan bir çocuk hakkındadır.

Yorumlar başlangıçta tasarımın korkunç bir fikir olduğu konusunda oldukça kritikti.

Bunun genel olarak en iyi fikir olmadığını biliyorum. Genel bir kuraldan, "Bunu yapma!" Demek adil gözüküyor.

Ancak, böyle bir şeyi yapmanız gereken hangi koşulların mevcut olacağını merak ediyorum. Buradaki soru ve ilgili cevaplar / yorumlar, grafiklerin bile böyle bir şey yapmamasını önerir.


1
Bağladığın soru bu konuda oldukça kapsamlı görünüyor.
Monica

4
@LightnessRacesinOrbit "bunu yapma" nedenini anlamak kadarıyla gerçekten yararlı değildir .
enderland

2
Orada "yapma" dan çok daha fazlasını görüyorum. Birden fazla uzman tarafından tartışılan artıları ve eksileri görüyorum.
Monica

1
Gezinme, bir tür dairesel tampon gerektiren iki yönlü bir listeye sahip olabilirsiniz, belki bir oyunda bağlı iki parçayı temsil ediyorsunuzdur - dairesel bir şeyi temsil etmeniz gerekirse, bu iyi bir fikir olabilir.
Ocak'ta 5:06

2
Benim pragmatik kuralımın bir sorusu “Bir Çocuk Ebeveyn Olmadan Var Olabilir mi?” Sorusudur. (XmlDocuments ve düğümlerini göz önünde bulundurursanız: bir düğüm, belgenin ağaç içeriği olmadan olamaz. Saçmalıktır). Cevabınız hayır ise iki yönlü bağlantılar iyidir : sadece birlikte var olabilen iki cisim vardır. Nesneler varsa olabilir bağımsız var, o zaman ben o iki bağlantılardan birini kaldırın.
mikalai

Yanıtlar:


43

Buradaki anahtar iki nesnenin dairesel referanslara sahip olup olmadığı değil, bu referansların birbirinin mülkiyetini gösterip göstermediğidir .

İki nesne birbirine "sahip olamaz": bu, başlatma ve silme sırası için anlaşılmaz bir ikileme neden olur. Birinin isteğe bağlı bir referans olması gerekir, aksi halde bir nesnenin diğerinin ömrünü yönetmeyeceğini belirtir.

İkili bağlantılı bir liste düşünün: iki düğüm birbiri ile ileri ve geri bağlanır, ancak ikisi de "diğerine" sahip değildir (liste her ikisine de sahiptir). Bu, hiçbir düğümün diğerine bellek ayırmadığı veya başkalarının kimliğinden veya ömür boyu yönetiminden sorumlu olmadığı anlamına gelmez.

Ağaçlar benzer bir ilişkiye sahiptir, ancak bir ağaçtaki düğümler çocukları tahsis edebilir ve ebeveynler kendi çocuklarına sahip olabilir. Çocuktan ebeveyne bağlantı, geçişe yardımcı olur, ancak yine de mülkiyeti tanımlamaz.

Çoğu OO tasarımında, bir nesnenin veri üyesi olarak başka bir nesneye yapılan bir referans , mülkiyeti ima eder. Örneğin, Car ve Engine sınıflarımız olduğunu varsayalım. Hiçbiri kendi başına çok faydalı değildir. Bu nesnelerin birbirine bağlı olduğunu söyleyebiliriz : faydalı çalışmalar yapmak için diğerinin varlığını gerektirir. Ama hangisi diğerine "sahip"? Bu durumda, Araba'nın Motorun sahibi olduğunu söyleyebiliriz çünkü araba, tüm otomotiv bileşenlerinin yaşadığı “konteyner” dir. Hem bir OO hem de gerçek dünya tasarımında, otomobil, parçalarının toplamıdır ve bu parçaların tümü otomobil bağlamında birbirine bağlanır. Motor, Araba’ya referans verebilir veya TorqueConverter’e referans verebilir

Dairesel referanslar olabilir ama mutlaka kötü bir tasarım Koku yüzünden. Akıllıca kullanıldığında ve doğru bir şekilde belgelendiğinde, veri yapılarını kullanmayı kolaylaştırabilirler.

Ebeveynler ve çocuklar arasında her iki yöne giden referanslar olmadan bir ağacı gezmeyi deneyin. Elbette, kırılgan ve karmaşık bir yığın temelli yaklaşım bulabilir veya önemsiz şekilde basit olan referans temelli bir yaklaşımı kullanabilirsiniz.


16

Böyle bir tasarımda dikkat edilmesi gereken birkaç husus vardır:

  • yapısal bağımlılıklar
  • mülkiyet ilişkisi (birleşme veya diğer birleşme türleri)
  • navigasyon ihtiyacı

Sınıflar arasındaki yapısal bağımlılık:

Bileşen sınıflarını yeniden kullanmayı amaçlıyorsanız, gereksiz bağımlılıktan kaçınmalı ve bu tür kapalı dairesel yapılardan kaçınmalısınız.

Bununla birlikte, bazen iki sınıf kavramsal olarak güçlü bir şekilde birbirine bağlıdır. Bu durumda, bağımlılıktan kaçınmak gerçek bir seçenek değildir. Örnek: bir ağaç ve yapraklar, veya daha genel olarak bir kompozit ve bileşenleri .

Nesnelerin mülkiyeti:

Bir nesne diğerine sahip mi? Ya da başka türlü ifade edilirse: Bir nesne imha edilirse diğeri de imha edilir mi?

Bu konu Snowman tarafından derinlemesine ele alındı, bu yüzden burada ele almayacağım.

Nesneler arasında gezinme ihtiyacı:

Son bir konu navigasyon ihtiyacıdır. En sevdiğim örnek, alalım kompozit tasarım deseni ait dört Gang .

Gama ve ark. Açık bir ana referansa sahip olma potansiyelinin açıkça belirtilmesi: “ Çocuk bileşenlerinden ana babalarına referansın muhafaza edilmesi, kompozit bir yapının geçişini ve yönetimini kolaylaştırabilir ” Tabii ki sistematik bir yukarıdan aşağıya geçişi düşünebilirsiniz, ancak çok büyük bileşik nesneler için işlemleri önemli ölçüde yavaşlatabilir ve üstel bir şekilde. Doğrudan bir referans, hatta dairesel bile, bileşiklerinizin manipülasyonunu önemli ölçüde kolaylaştırır.

Örnek bir elektronik sistemin grafik modeli olabilir. Kompozit bir yapı elektronik panoları, devreleri, elemanları temsil edebilir. Modeli görüntülemek ve değiştirmek için GUI görünümünde bazı geometrik proxy'lere ihtiyacınız olacaktır. Bu durumda, kullanıcı tarafından seçilen GUI öğesinden bileşene gitmek, hangisinin ebeveyn ve kiminle ilişkili olduğunu bulmak, yukarıdan aşağıya bir arama başlatmaktan çok daha kolaydır.

Elbette, Gamma’nın da belirttiği gibi, döngüsel ilişkinin değişmezlerini sağlamak zorundasınız. Bahsettiğiniz SO sorusunun göstermiş olduğu gibi, bu zor olabilir. Ama mükemmel şekilde yönetilebilir ve güvenli bir şekilde.

Sonuç

Navigasyon ihtiyacı anlaşılmayacaktır. UML'nin modelleme notasyonunda açıkça dile getirilmiş olması sebepsiz değildir. Ve evet, dairesel referanslara ihtiyaç duyulan tamamen geçerli bir durum var.

Tek nokta bazen insanların çabucak böyle bir yöne gitme eğiliminde olmalarıdır. Bu nedenle, karar almaya karar vermeden önce dahil olan tüm 3 yönü göz önünde bulundurmaya değer.


1
Bu cevap IMHO'nun ağır bir şekilde bozulmamış olması. OP sordu: “ Zaten bir ebeveyn-çocuk ilişkisi olduğu göz önüne alındığında , bunu dairesel bir referansla uygulamak ne zaman uygundur? Dolayısıyla yapı ve “mülkiyet” (burada belirtilen anlamda) zaten açıktır. Bu , bir tarafa ya da diğerine referanslar eklemek için tek kriter "gezinme ihtiyaçları" ve "çocuğun bağımsız kullanımı" ile ilgili sorulardır.
Doktor Brown

@DocBrown - Bu soruya uygun olduktan sonra her zaman bir ödül koyabilirsiniz. :-)

1
@ GlenH7: bu cevabı daha fazla oy vermezdi, sadece Snowman'ın cevabı (sanırım sorunun bir kısmını özlediğini düşünüyorum).
Doktor Brown,

1
@DocBrown - Ama repz, dostum, repz!

... ve kamu-özel korumalı gibi görünürlük seçenekleri eklerseniz, bu size 9 farklı seçenek sunar :)
mikalai

8

Genelde, dairesel referanslar çok kötü bir fikirdir çünkü bunlar dairesel bağımlılıklar anlamına gelir. Muhtemelen döngüsel bağımlılıkların neden kötü olduğunu zaten biliyorsunuzdur, ancak bütünlük uğruna, tl; dr sürümü, A ve B sınıflarının her ikisinin de birbirine bağlı olduğu durumlarda, aynı zamanda A veya B'yi de anlamadan / düzeltmek / optimize etmek / vb. Diğer sınıfı aynı anda anlamak / tespit etmek / optimize etmek. Bu da hızlı bir şekilde her şeyi değiştirmeden hiçbir şeyi değiştiremeyeceğiniz kod tabanlarına götürür.

Ancak, bir kötülük dairesel bağımlılıkları yaratmadan döngüsel bir başvuru olması mümkün. Bu, işlevsel olarak tamamen isteğe bağlı isteğe bağlı olduğu sürece çalışır . Demek istediğim, onu sınıflardan kolayca çıkarabiliyorsunuz ve daha yavaş çalışsalar bile yine de çalışıyorlardı. Bağımlılık yaratan bu tür dairesel olmayan referansların farkında olduğum ana kullanım durumu, bağlantılı listeler, ağaçlar ve yığınlar gibi düğüm tabanlı veri yapılarının hızlı bir şekilde dolaşmasını sağlamaktır. Örneğin, prensip olarak iki bağlantılı bir listede gerçekleştirebileceğiniz herhangi bir işlemi tek bir bağlı listede de gerçekleştirebilirsiniz, sadece çok daha iyi bir büyüklüğe sahip birkaç liste (listede geriye doğru hareket etmek gibi) olur. O iki kat bağlı versiyonu ile.


6

Bunu yapmanın genellikle iyi bir fikir olmaması nedeni, Bağımlılık İnversiyon Prensibi'ni ihlal etmesidir . İnsanlar bu yazıda yeterince kapatabileceğimden çok daha ayrıntılı olarak bu konuda çok şey yazdılar, ancak bağlantı çok sıkı olduğu için bakımı zorlaştırıyor. Her iki sınıfın değiştirilmesi neredeyse her zaman diğerinde bir değişikliğe ihtiyaç duyar, oysa bağımlılıklar yalnızca bir yolu işaret ederse, arayüzün bir tarafındaki değişiklikler izole edilir. Her iki sınıf da soyut bir arayüze işaret ediyorsa, daha da iyi.

Bunun tek istisnası, farklı soyutlama seviyelerinde iki farklı sınıfa sahip olmamanız, ancak aynı sınıftaki iki düğüm, örneğin bir ağaçta, iki kat bağlantılı bir listeden vb. soyutlama ilişkisi. Algoritmik verim uğruna dairesel referanslar kabul edilebilir ve bu tür durumlarda bile teşvik edilmektedir.


5

[...] grafiklerin bile böyle bir şey yapmamasını önerir.

Bazen şeylere ağaçtan farklı bir veri yapısından aşağıdan yukarıya doğru bir şekilde erişmeniz gerekirken, ağacın tepeden aşağı doğru bir şekilde erişmesi gerekir.

Örnek olarak, bir dörtgen, elemanları bir vektör grafik yazılımında saklayabilir. Bununla birlikte, kullanıcının seçimi, vektörel eleman referansları / işaretçilerden oluşan ayrı bir seçim listesine kaydedilir. Kullanıcı bu seçimi silmek istediğinde, çeyreği güncellememiz gerekir ve bu ağaç yukarıdan ziyade yaprakları aşağıdan yukarıya doğru aşağıdan yukarıya doğru güncellemek için çok daha etkili olabilir. Aksi halde, her bir element için kökten yaprağa doğru çalışmanız ve sonra tekrar yedeklemeniz gerekir.


1
Evet, bu örnek gerçekten uygun. Aynen, bir kompozit içerisinde navigasyon hakkındaki argümanımda anlatmaya çalıştığım şeyler: backpointer, hızı önemli ölçüde hızlandırıyor. İlginç performans anekdotu ve çok net bir şema için teşekkür ederiz! +1
Christophe,

@ Christophe Örneğinizi de beğendim! Soruyu tam olarak cevaplayabildiğimden emin değildim, çünkü belki de "dairesel mülkiyet" ile ilgili bir veri yapısını yukarı / geriye kaydırmamıza izin veren geri işaretçilerden daha fazlasıydı. Ama ben esas olarak sorunun son "grafik" kısmına cevap veriyordum.

2

Doom 3, üst nesneye imleci olan bir alt nesne örneğine sahiptir. Özellikle müdahaleci listeleri kullanır . Özetlemek gerekirse, müdahaleci bir liste, her düğüm listenin kendisine bir işaretçi içermesi dışında bağlantılı bir liste gibidir.

Avantajları:

  • Nesneler aynı anda birkaç listede bulunabilirse, liste düğümlerinin hafızasının yalnızca bir kez tahsis edilmesi ve çıkarılması gerekir.

  • Bir nesnenin imha edilmesi gerektiğinde, her listeyi doğrusal olarak aramadan, listedeki tüm listelerden kolayca kaldırabilirsiniz.

Bunun oldukça özel bir senaryo olduğunu düşünüyorum, ancak sorunuzu anlarsam, üst nesneye bir işaretçi içeren bir alt nesnenin kabul edilebilir bir kullanımına bir örnek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.