C ++ kodumda sınıf bağımlılığı nasıl giderilir?


10

C ++ projemde iki sınıfım var Particleve Contact. Olarak Particlesınıf, bir üye değişkeni olan std::vector<Contact> contactsbir bütün temas içeren Particlebir nesne ve karşılık gelen üye işlevlerini getContacts()ve addContact(Contact cont). Bu nedenle, "Particle.h" içinde, "Contact.h" yi içerir.

In Contactsınıfında, ben kurucusuna kodu eklemek istiyorum Contacto arayacak Particle::addContact(Contact cont)böylece contactsher ikisi için güncellenir Particlehangi arasındaki nesneler Contactnesne ekleniyor. Bu nedenle, "Contact.cpp" içine "Particle.h" eklemem gerekir.

Benim sorum, bunun kabul edilebilir / iyi kodlama uygulaması olup olmadığıdır ve eğer değilse, elde etmeye çalıştığım şeyi uygulamak için daha iyi bir yol ne olacaktır (basitçe, yeni bir kişi olduğunda belirli bir parçacık için kişi listesini otomatik olarak güncelleme yaratıldı).


Bu sınıflar, NetworkN parçacıkları ( std::vector<Particle> particles) ve Nc kontakları ( std::vector<Contact> contacts) olacak bir sınıf tarafından birbirine bağlanır . Ama ben particles[0].getContacts()bu gibi Particlesınıfta böyle fonksiyonlara sahip olmak için iyi mi , ya da bu durumda C ++ daha iyi bir ilişki "yapısı" var mı istedim (başka bir sınıfta kullanılan ilgili iki sınıf) .


Burada buna nasıl yaklaştığım konusunda bir perspektif değişikliğine ihtiyacım olabilir. İki sınıf bir Networksınıf nesnesi tarafından bağlandığından , bağlantı bilgilerinin tamamen Networknesne tarafından kontrol edilmesi tipik bir kod / sınıf organizasyonu mudur (bir Particle nesnesinin temaslarının farkında olmaması ve dolayısıyla bir getContacts()üyesi olmaması gerekir) fonksiyonu). Daha sonra, belirli bir parçacığın hangi temas noktalarına sahip olduğunu bilmek için, bu bilgiyi Networknesne aracılığıyla elde etmem gerekir (örn network.getContacts(Particle particle). Kullanarak ).

Bir Parçacık nesnesinin bu bilgiye sahip olması daha az tipik (belki de cesareti kırılmış) C ++ sınıf tasarımının da (yani, bilgiye ne olursa olsun - Ağ nesnesi veya Parçacık nesnesi aracılığıyla - hangisi daha uygun görünüyorsa) )?


4
İşte cppcon 2017'den bir başlık - Üç Katman Üstbilgisi: youtu.be/su9ittf-ozk
Robert Andrzejuk

3
"En iyi", "daha iyi" ve "kabul edilebilir" gibi kelimeler içeren sorular, belirli değerlendirme ölçütlerinizi
Robert Harvey

Düzenleme için teşekkürler, ancak "tipik" ifadenizi değiştirmek sadece popülerlik meselesi yapar. Kodlamanın şu ya da bu şekilde yapılmasının nedenleri vardır ve popülerlik bir tekniğin "iyi" olduğunun bir göstergesi ("iyi" nin bazı tanımları için) olsa da, aynı zamanda kargo-kültüre ilişkin bir gösterge de olabilir.
Robert Harvey

@RobertHarvey Son bölümümde "daha iyi" ve "kötü" ifadesini kaldırdım. Nesneler ve nesneler Networkiçeren bir sınıf nesneniz olduğunda tipik (belki de tercih edilen / teşvik edilen) yaklaşımı istiyorum . Bu temel bilgiyle, projeye devam ederken hala araştırılmakta / geliştirilmekte olan özel ihtiyaçlarıma uyup uymadığını değerlendirmeye çalışabilirim. ParticleContact
AnInquiringMind

@RobertHarvey C ++ projelerini tamamen "tipik" ve "popüler" olanı öğrenmekle iyiyim sıfırdan yazmaya yetecek kadar yeniyim. Umarım bir noktada başka bir uygulamanın neden daha iyi olduğunu anlayabilmek için yeterli fikir sahibi olurum, ancak şimdilik sadece buna tamamen kemik kafalı bir şekilde yaklaşmadığımdan emin olmak istiyorum!
AnInquiringMind

Yanıtlar:


17

Sorunuzda iki bölüm var.

İlk bölüm C ++ başlık dosyalarının ve kaynak dosyalarının organizasyonudur. Bu, ileri bildirimi ve sınıf bildiriminin (başlık dosyasına yerleştirilmesi) ve yöntem gövdesinin (kaynak dosyaya yerleştirilmesi) ayrılması kullanılarak çözülür . Ayrıca, bazı durumlarda daha zor vakaları çözmek için Pimpl deyimini ("uygulamaya işaretçi") uygulayabiliriz . En iyi uygulamalara göre, paylaşılan sahiplik işaretçileri ( shared_ptr), tek sahiplik işaretçileri ( unique_ptr) ve sahip olmayan işaretçiler (ham işaretçi, yani "yıldız işareti") kullanın.

İkinci bölüm, bir grafik şeklinde birbiriyle ilişkili nesnelerin nasıl modelleneceğidir . DAG olmayan genel grafiklerin (yönlendirilmiş asiklik grafikler), ağaç benzeri sahipliği ifade etmenin doğal bir yolu yoktur. Bunun yerine, düğümlerin ve bağlantıların tümü tek bir grafik nesnesine ait olan meta verilerdir. Bu durumda, düğüm-bağlantı ilişkisini toplama olarak modellemek mümkün değildir. Düğümler "kendi" bağlantılarına sahip değildir; bağlantılar "kendi" düğümlerine sahip değildir. Bunun yerine, ilişkilendirmelerdir ve hem düğümler hem de bağlantılar grafiğe "aittir". Grafik, düğümler ve bağlantılar üzerinde çalışan sorgulama ve düzenleme yöntemleri sağlar.


Yanıtınız için teşekkürler! Aslında N parçacıkları ve Nc temas olacak bir Ağ sınıfı var. Ama şu gibi işlevlere sahip olmak istedim particles[0].getContacts()- son paragrafınızda Particlesınıfta bu tür işlevlere sahip olmamam gerektiğini mi yoksa şu anki yapının iyi olduğunu çünkü doğası gereği ilgili / ilişkili olduklarını mı söylüyorsunuz Network? Bu durumda C ++ 'da daha iyi bir ilişki "yapısı" var mı?
AnInquiringMind

1
Genellikle Ağ, nesneler arasındaki ilişkileri bilmekle yükümlüdür. Örneğin, bir bitişiklik listesi kullanırsanız, parçacık , kontaklarının indekslerine network.particle[p]karşılık gelir network.contacts[p]. Aksi takdirde, Ağ ve Parçacık bir şekilde aynı bilgileri izliyor.
Yararsız

@Useless Evet, nasıl ilerleyeceğimden emin değilim. Yani, Particlenesnenin temaslarının farkında olmaması gerektiğini söylüyorsunuz (bu yüzden bir getContacts()üye fonksiyonuna sahip olmamalıyım ) ve bu bilginin sadece Networknesnenin içinden gelmesi gerektiğini mi söylüyorsunuz ? Bir Particlenesnenin bu bilgiye sahip olması (yani, bilgiye Networkveya Particlenesneye, hangisi daha uygun görünüyorsa) erişmek için birden çok yolu olması kötü bir C ++ sınıfı tasarım mıdır? İkincisi benim için daha anlamlı görünüyor, ama belki de bu konudaki bakış açımı değiştirmem gerekiyor.
AnInquiringMind

1
@PhysicsCodingEnthusiast: S veya s Particlehakkında bir şey bilmekle ilgili sorun, sizi bu ilişkiyi temsil etmenin belirli bir yoluna bağlamasıdır . Her üç sınıf da aynı fikirde olabilir. Bunun yerine, bilen veya önemseyen tek kişi ise, başka bir temsilin daha iyi olduğuna karar verirseniz değişmesi gereken sadece bir sınıftır. ContactNetworkNetwork
cHao

@cHao Tamam, bu mantıklı. Bu yüzden Particleve Contacttamamen ayrı olmalıdır ve aralarındaki ilişki Networknesne tarafından tanımlanır . Tam olarak emin olmak için, @rwong yazdığında (muhtemelen) bu (muhtemelen), "hem düğümler hem de bağlantılar grafiğe" aittir. Grafik, düğümler ve bağlantılar üzerinde çalışan sorgulama ve düzenleme yöntemleri sağlar. " , sağ?
AnInquiringMind

5

Eğer seni haklıyorsam, aynı temas nesnesi birden fazla parçacık nesnesine aittir, çünkü iki veya daha fazla parçacık arasındaki bir tür fiziksel teması temsil eder, değil mi?

Şüpheli olduğunu düşündüğüm ilk şey neden Particlebir üye değişkene sahip std::vector<Contact>? Bunun yerine bir std::vector<Contact*>veya bir olmalıdır std::vector<std::shared_ptr<Contact> >. veya bunun addContactgibi farklı imzalara sahip olmalıdır .addContact(Contact *cont)addContact(std::shared_ptr<Contact> cont)

Bu, "Particle.h" içine "Contact.h" ifadesini eklemeyi gereksiz kılar, "Particle.h" içinde ileri bir bildirim ve class Contact"Particle.cpp" içinde "Contact.h" ifadesini eklemek yeterli olacaktır.

Sonra kurucu hakkında soru. Gibi bir şey istiyorsun

 Contact(Particle &p1, Particle &p2)
 {
      p1.addContact(this);
      p2.addContact(this);
 }

Sağ? Bu tasarım, programınız bir temas nesnesinin oluşturulması gereken zamanda her zaman ilgili parçacıkları bildiği sürece tamamdır.

std::vector<Contact*>Rotaya giderseniz , Contactnesnelerin ömrü ve mülkiyeti hakkında bazı düşünceler yatırmanız gerekir . Hiçbir parçacık kendi kişilerine "sahip değildir", bir kişinin yalnızca ilgili iki Particlenesne de yok edildiğinde silinmesi gerekecektir . Kullanılması std::shared_ptr<Contact>sizin için otomatik olarak bu sorunu çözecektir yerine. Ya da bir "çevreleyen bağlam" nesnesinin parçacıkların ve temasların (@rwong tarafından önerildiği gibi) sahipliğini almasına ve ömürlerini yönetmesine izin vermiş olursunuz.


addContact(const std::shared_ptr<Contact> &cont)Aşırı fayda görmüyorum addContact(std::shared_ptr<Contact> cont)?
Caleth

@Caleth: Bu burada tartışıldı: stackoverflow.com/questions/3310737/… - "const" burada gerçekten önemli değil, ancak nesneleri referansla (ve değere göre skaler) geçirmek C ++ 'da standart deyimdir.
Doc Brown

2
Bu cevapların birçoğu bir moveparadigmadan geliyor gibi görünüyor
Caleth

@Caleth: tamam, tüm nitpickers'ları mutlu etmek için cevabımın bu oldukça önemsiz kısmını değiştirdim.
Doc Brown

1
@PhysicsCodingEnthusiast: Hayır, bu yapma konusunda en önemli olan particle1.getContacts()ve particle2.getContacts()bunları verme Contactarasındaki fiziksel teması temsil eden bir nesne particle1ve particle2iki farklı nesne, olup. Tabii ki, Contactaynı fiziksel teması temsil eden aynı anda iki nesne olup olmadığı sistemi önemli olmayacak şekilde tasarlamaya çalışılabilir . Bu Contactdeğişmez hale getirmeyi gerektirir , ancak bunun istediğinizden emin misiniz?
Doc Brown

0

Evet, açıkladığınız şey, her Contactörneğin bir a'nın kişiler listesinde olmasını sağlamak için kabul edilebilir bir yoldur Particle.


Yanıtınız için teşekkürler. Bir çift birbirine bağımlı sınıf (örneğin, MS Joshi tarafından "C ++ Tasarım Desenleri ve Türev Fiyatlandırma") kaçınılması gereken bazı öneriler okumuştu, ama görünüşe göre bu doğru değil mi? Meraktan, belki de bu otomatik güncellemeyi karşılıklı bağımlılığa ihtiyaç duymadan uygulamanın başka bir yolu var mı?
17:05

4
@PhysicsCodingEnthusiast: Birbirine bağlı sınıflara sahip olmak her türlü zorluğu yaratır ve bunlardan kaçınmaya çalışmalısınız. Ancak bazen, iki sınıf birbiriyle o kadar yakından ilişkilidir ki, aralarındaki karşılıklı bağımlılığı ortadan kaldırmak, bağımlılığın kendisinden daha fazla soruna neden olur.
Bart van Ingen Schenau

0

Yaptığınız doğru.

Başka bir yol ... Amaç, her Contactşeyin bir listede olmasını sağlamaksa , şunları yapabilirsiniz:

  • Contact(özel inşaatçılar) oluşturulmasını engelle ,
  • ileri deklare Particlesınıfı,
  • yapmak Particlesınıf arkadaşı Contact,
  • içinde Particlea oluşturan bir fabrika yöntemini oluşturmakContact

Sonra dahil etmek gerekmez particle.hiçindecontact


Yanıtınız için teşekkürler! Bunu uygulamanın faydalı bir yolu gibi görünüyor. Sınıfla ilgili ilk soruya yaptığım düzenlemeyle Network, bunun önerilen yapıyı değiştirdiğini mi yoksa hala aynı mı olacağını merak ediyorum.
AnInquiringMind

Sorunuzu güncelledikten sonra, kapsamı değişiyor. ... Şimdi daha önce teknik bir sorun olduğunda uygulamanızın mimarisini soruyorsunuz.
Robert Andrzejuk

0

Düşünebileceğiniz başka bir seçenek de, bir Particle referansını kabul eden Contact yapıcısını şablon haline getirmektir. Bu, bir Kişinin kendisini uygulayan herhangi bir kaba eklemesine izin verecektir addContact(Contact).

template<class Container>
Contact(/*parameters*/, Container& container)
{
  container.addContact(*this);
}
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.