İki sınıf arasında çoktan çoğa ilişkiyi temsil etmenin iyi bir yolu nedir?


10

Diyelim ki A ve B olmak üzere iki nesne tipim var. Aralarındaki ilişki çoktan çoğa, ama ikisi de diğerinin sahibi değil.

A ve B örneklerinin her ikisi de bağlantının farkında olmalıdır; bu sadece bir yol değil.

Yani, bunu yapabiliriz:

class A
{
    ...

    private: std::vector<B *> Bs;
}

class B
{
    private: std::vector<A *> As;
}

Sorum şu: bağlantıları oluşturmak ve yok etmek için fonksiyonları nereye koyabilirim?

A :: Bs ve B :: vektör olarak güncellenen A :: Ek (B) olmalı mı?

Ya da eşit derecede makul görünen B :: Attach (A) olmalı.

Bunların hiçbiri doğru hissetmiyor. Kodla çalışmayı bırakırsam ve bir hafta sonra geri gelirsem, A.Attach (B) veya B.Attach (A) yapmam gerektiğini hatırlayamayacağımdan eminim.

Belki de böyle bir işlev olmalıdır:

CreateConnection(A, B);

Ancak, sadece A ve B sınıflarıyla çalışmak için bir işlev olduğu için, küresel bir işlev yapmak da istenmeyen bir durumdur.

Başka bir soru: sık sık bu sorun / gereksinim ile karşılaşırsam, bir şekilde genel bir çözüm yapabilir miyim? Belki de türetebileceğim veya bu tür bir ilişkiyi paylaşan sınıflar içinde kullanabileceğim bir TwoWayConnection sınıfı?

Bu durumla başa çıkmanın bazı iyi yolları nelerdir ... Bire-çok "C'nin D sahibi" durumunun oldukça iyi nasıl idare edileceğini biliyorum, ama bu daha zor.

Edit: Sadece daha açık yapmak için, bu soru sahiplik sorunları içermez. Hem A hem de B başka bir Z nesnesine aittir ve Z tüm sahiplik sorunlarını ele alır. Sadece A ve B arasındaki çoktan çoğa bağlantıları nasıl oluşturacağım / kaldıracağım.


Ben bir Z :: Attach (A, B) yaratırdım, bu yüzden eğer Z iki çeşit nesneye sahipse, “doğru hissettirir”, bağlantıyı yaratması için. (Çok iyi olmayan) örneğimde Z, A ve B için bir depo olurdu. Pratik bir örnek olmadan başka bir yol göremiyorum.
Machado

1
Daha kesin olmak gerekirse, A ve B'nin farklı sahipleri olabilir. Belki A, Z'ye aitken, B, Z'ye ait olan X'e aittir. Gördüğünüz gibi, bağlantıyı başka bir yerde yönetmeye çalışırken gerçekten kıvrımlı hale gelir. A ve B'nin bağlı olduğu çiftlerin listesine hızlı bir şekilde erişebilmesi gerekir.
Dmitri Shuralyov

Benim durumumda A ve B'nin gerçek isimlerini merak ediyorsanız, onlar Pointerve GestureRecognizer. İşaretçiler, InputManager sınıfına aittir ve bu yönetim tarafından yönetilir. GestureRecognizers, bir App örneğine ait olan bir Screen örneğine ait olan Widget örneklerine aittir. İşaretçiler, GestureRecognizers'a ham giriş verilerini besleyebilmeleri için atanır, ancak GestureRecognizers'ın şu anda kendileriyle kaç tane işaretçi ilişkili olduğunun farkında olması gerekir (1 parmakla 2 parmak hareketini vb. Ayırt etmek).
Dmitri Shuralyov

Şimdi bunun hakkında daha fazla düşündüğüme göre, sadece çerçeve tabanlı (işaretçi tabanlı değil) jest tanıma yapmaya çalışıyorum. Bu yüzden olayları bir seferde ibre yerine her seferinde bir kareye hareket ettirebilirim. Hmm.
Dmitri Shuralyov

Yanıtlar:


2

Bunun bir yolu, her sınıfa bir genel Attach()yöntem ve korumalı bir AttachWithoutReciprocating()yöntem eklemektir . Make Ave Bonların böylece ortak arkadaşlarımız Attach()yöntemleri diğer 's çağırabilir AttachWithoutReciprocating():

A::Attach(B &b) {
    Bs.push_back(&b);
    b.AttachWithoutReciprocating(*this);
}

A::AttachWithoutReciprocating(B &b) {
    Bs.push_back(&b);
}

Benzer yöntemleri uygularsanız Bhangi sınıfı arayacağınızı hatırlamanız gerekmez Attach().

Eminim bir o davranışı sarabilirdiniz olduğum MutuallyAttachablesınıfın her iki Ave Bmiras böylece kendini tekrar ve Hesap Günü bonus puan puanlama kaçınarak dan. Ancak her iki yerde de sofistike olmayan uygulama yöntemi bile işi halledecek.


1
Bu tam olarak aklımın arkasında düşündüğüm çözüme benziyor (yani Attach () 'ı A ve B'ye koymak). Ayrıca bu davranış gerektiğinde türetebileceğiniz MutuallyAttachable sınıfına sahip olmanın daha DRY çözümünü (gerçekten yinelenen kodu sevmiyorum) seviyorum (oldukça sık öngörüyorum). Başka biri tarafından sunulan aynı çözümü görmek beni bu konuda çok daha güvenli kılıyor, teşekkürler!
Dmitri Shuralyov

Bunu kabul etmek, çünkü IMO şimdiye kadar sunulan en iyi çözüm. Teşekkürler! MutuallyAttachable sınıfını şimdi uygulayacağım ve öngörülemeyen herhangi bir sorunla karşılaşırsam burada rapor edeceğim (henüz çok fazla deneyimim olmayan birden fazla kalıtım nedeniyle bazı zorluklar da ortaya çıkabilir).
Dmitri Shuralyov

Her şey yolunda gitti. Ancak, orijinal soru hakkındaki son yorumuma göre, başlangıçta düşündüğüm şey için bu işlevselliğe ihtiyacım olmadığını fark etmeye başladım. Bu 2 yollu bağlantıları takip etmek yerine, her çifti ihtiyaç duyan işleve parametre olarak geçireceğim. Ancak, bu daha sonra kullanışlı olabilir.
Dmitri Shuralyov

İşte MutuallyAttachablebu görev için yazdığım sınıf, eğer birisi onu yeniden kullanmak istiyorsa: goo.gl/VY9RB (Pastebin) Edit: Bu kod bir şablon sınıfı haline getirilerek geliştirilebilir.
Dmitri Shuralyov

1
MutuallyAttachable<T, U>Sınıf şablonunu Gist'e yerleştirdim . gist.github.com/3308058
Dmitri Shuralyov

5

İlişkinin kendisinin ek özelliklere sahip olması mümkün müdür ?

Eğer öyleyse, o zaman ayrı bir sınıf olmalıdır.

Değilse, herhangi bir ileri geri hareketli liste yönetim mekanizması yeterli olacaktır.


Eğer ayrı bir sınıfsa, A bağlantılı B örneklerini ve B bağlantılı A örneklerini nasıl bilir? Bu noktada, hem A hem de B'nin C ile 1'den birçok ilişkiye sahip olması gerekirdi, değil mi?
Dmitri Shuralyov

1
A ve B'nin her ikisinin de C örneklerinin toplanması için bir referansa ihtiyacı olacaktır; C ilişkisel modellemede 'köprü tablosu' ile aynı amaca hizmet eder
Steven A. Lowe

1

Genellikle garip hissettiren bu tür durumlara girdiğimde, nedenini tam olarak söyleyemiyorum, çünkü yanlış bir sınıfa bir şey koymaya çalışıyorum. Neredeyse her zaman bazı işlevleri sınıftan çıkarmanın Ave Bişleri daha net hale getirmenin bir yolu vardır .

İlişkilendirmeyi taşımaya aday olanlardan biri, ilişkilendirmeyi ilk etapta oluşturan koddur. Belki çağırmak yerine attach()sadece bir listesini saklar std::pair<A, B>. İlişkilendirmeler oluşturan birden fazla yer varsa, bir sınıfa ayrılabilir.

Bir hamle için başka bir aday Zda sizin durumunuzda ilişkili nesnelere sahip olan nesnedir .

İlişkilendirmeyi taşımak için kullanılan bir diğer yaygın aday da, yöntemleri veya nesneleri sık sık çağıran koddur . Örneğin, tüm ilişkili nesnelerle dahili olarak bir şeyler yapan çağrı yerine, her biri için ilişkilendirmeleri ve çağrıları zaten biliyor . Yine, birden fazla yer onu çağırırsa, tek bir sınıfa soyutlanabilir.ABa->foo()Ba->foo(b)

İlişkilendirmeyi oluşturan, sahip olan ve kullanan nesnelerin çoğu kez aynı nesne olur. Bu, yeniden düzenlemeyi çok kolaylaştırabilir.

Son yöntem, ilişkiyi diğer ilişkilerden türetmektir. Örneğin, kardeşler çoktan çoğa bir ilişkidir, ancak kardeş sınıfında kız kardeşlerin bir listesini tutmak yerine tam tersi, bu ilişkiyi ebeveyn ilişkisinden alırsınız. Bu yöntemin diğer avantajı, tek bir gerçek kaynağı yaratmasıdır. Bir hata veya çalışma zamanı hatasının kardeş, kız kardeş ve ebeveyn listeleri arasında tutarsızlık yaratmasının olası bir yolu yoktur, çünkü yalnızca bir listeye sahipsiniz.

Bu tür yeniden düzenleme, her seferinde işe yarayan sihirli bir formül değildir. Her bir durum için iyi bir uyum bulana kadar denemeniz gerekiyor, ancak zamanın yaklaşık% 95'inde işe yaradığını buldum. Kalan zamanlar sadece beceriksizlikle yaşamak zorundasın.


0

Bunu uyguluyor olsaydım, Attach yönteminin her ikisine de Attach yönteminin içine Attach koyardım, daha sonra iletilen nesneye Attach'ı çağırırdım. A). Sözdizim doğru olmayabilir, yıllardır c ++ kullanmadım:

class A {
  private: std::vector<B *> Bs;

  void Attach (B* obj) {
    // Only add obj if it's not in the vector
    if (std::find(Bs.begin(), Bs.end(), obj) == Bs.end()) {
      //Add obj to the vector
      Bs.push_back(obj);

      // Attach this class to obj
      obj->Attach(this);
    }
  }    
}

class B {
  private: std::vector<A *> As;

  void Attach (A* obj) {
    // Only add obj if it's not in the vector
    if (std::find(As.begin(), As.end(), obj) == As.end()) {
      //Add obj to the vector
      As.push_back(obj);

      // Attach this class to obj
      obj->Attach(this);
    }
  }    
}

Alternatif bir yöntem, AB eşleşmelerinin statik bir listesini yöneten 3. sınıf bir C oluşturmaktır.


AB çiftlerinin bir listesini yönetmek için 3. sınıf C'ye sahip olmanın alternatif önerisiyle ilgili bir soru: A ve B, çift üyelerini nasıl bilebilir? Her A ve B'nin (tek) C'ye bir bağlantıya ihtiyacı var mı, sonra C'den uygun çiftleri bulmasını ister misiniz? B :: Foo () {std :: vektör <A *> As = C.GetAs (bu); / * As ile bir şeyler yap ... * /} Yoksa başka bir şey öngördün mü? Çünkü bu çok kıvrımlı görünüyor.
Dmitri Shuralyov
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.