दो वर्गों के बीच कई-से-कई संबंधों का प्रतिनिधित्व करने का एक अच्छा तरीका क्या है?


10

मान लीजिए कि मेरे पास दो ऑब्जेक्ट प्रकार हैं, ए और बी। उनके बीच संबंध कई-से-कई हैं, लेकिन दोनों में से कोई भी दूसरे का मालिक नहीं है।

ए और बी दोनों उदाहरणों को कनेक्शन के बारे में पता होना चाहिए; यह सिर्फ एक तरीका नहीं है।

तो, हम यह कर सकते हैं:

class A
{
    ...

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

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

मेरा सवाल यह है: मैं कनेक्शन बनाने और नष्ट करने के लिए कार्य कहां करूं?

क्या यह A :: Attach (B) होना चाहिए, जो तब A :: Bs और B :: वैक्टर के रूप में अपडेट होता है?

या यह बी :: अटैच (ए) होना चाहिए, जो समान रूप से उचित लगता है।

न ही उनमें से कोई सही लगता है। अगर मैं कोड के साथ काम करना बंद कर दूं, और एक हफ्ते बाद वापस आऊं, तो मुझे यकीन है कि मैं ए। एटाच (बी) या बी.आट्टाच (ए) कर रहा हूं तो मुझे याद नहीं होगा।

शायद यह इस तरह से एक समारोह होना चाहिए:

CreateConnection(A, B);

लेकिन एक वैश्विक कार्य करना भी अवांछनीय लगता है, यह देखते हुए कि यह विशेष रूप से केवल ए और बी के साथ काम करने के लिए एक फ़ंक्शन है।

एक और सवाल: अगर मैं इस समस्या / आवश्यकता में अक्सर भाग जाता हूं, तो क्या मैं किसी तरह इसके लिए एक सामान्य समाधान कर सकता हूं? शायद एक TwoWayConnection वर्ग जिसे मैं इस प्रकार के संबंधों को साझा करने वाली कक्षाओं के भीतर से प्राप्त कर सकता हूं या उपयोग कर सकता हूं?

इस स्थिति को संभालने के लिए कुछ अच्छे तरीके क्या हैं ... मुझे पता है कि कैसे एक-से-कई "सी का मालिक डी" स्थिति को अच्छी तरह से संभालना है, लेकिन यह एक मुश्किल है।

संपादित करें: इसे और अधिक स्पष्ट करने के लिए, इस प्रश्न में स्वामित्व के मुद्दे शामिल नहीं हैं। A और B दोनों ही किसी अन्य ऑब्जेक्ट Z के स्वामित्व में हैं, और Z सभी स्वामित्व मुद्दों का ध्यान रखता है। मैं केवल ए और बी के बीच कई-से-कई लिंक बनाने / हटाने में दिलचस्पी रखता हूं।


मैं एक जेड :: अटैच (ए, बी) बनाऊंगा, इसलिए, यदि जेड दो प्रकार की वस्तुओं का मालिक है, तो कनेक्शन बनाने के लिए "सही लगता है"। मेरे (नहीं-तो-अच्छा) उदाहरण में, Z ए और बी दोनों के लिए एक रिपॉजिटरी होगा, मैं एक और उदाहरण के बिना एक और उदाहरण नहीं देख सकता।
मचाडो

1
अधिक सटीक होने के लिए, ए और बी के अलग-अलग मालिक हो सकते हैं। शायद A, Z के स्वामित्व में है, जबकि B का स्वामित्व X के पास है, जो Z के स्वामित्व में है। जैसा कि आप देख सकते हैं, यह तब लिंक को प्रबंधित करने की कोशिश में वास्तव में जटिल हो जाता है। A और B को अपने द्वारा जोड़े जाने वाले जोड़ों की सूची तक शीघ्र पहुँचने में सक्षम होने की आवश्यकता है।
दिमित्री शूर्योव

यदि आप मेरी स्थिति में ए और बी के वास्तविक नामों के बारे में उत्सुक हैं, तो वे हैं Pointerऔर GestureRecognizer। संकेत इनपुटमैनगर वर्ग के स्वामित्व और प्रबंधित होते हैं। GestureRecognizers विजेट्स उदाहरणों के स्वामित्व में हैं, जो स्क्रीन उदाहरण के स्वामित्व में हैं, जो ऐप इंस्टेंस के स्वामित्व में है। पॉइंटर्स को GureureRecognizers को सौंपा गया है ताकि वे उन्हें कच्चा इनपुट डेटा फीड कर सकें, लेकिन GestureRecognizers को इस बात से अवगत रहने की आवश्यकता है कि वर्तमान में उनके साथ कितने पॉइंटर्स जुड़े हैं (1 उंगली बनाम 2 उंगली इशारों को भेद करने के लिए)।
दिमित्री शूर्योव

अब जब मैं इसके बारे में अधिक सोचता हूं, तो ऐसा लगता है कि मैं केवल फ़्रेम-आधारित (पॉइंटर-आधारित) इशारा पहचान करने की कोशिश कर रहा हूं। इसलिए मैं केवल पॉइंटर-ए-ए-टाइम के बजाय फ्रेम-ए-ए-टाइम के लिए ईवेंट पास कर सकता हूं। हम्म।
दिमित्री शूर्योव

जवाबों:


2

एक तरीका यह है कि प्रत्येक वर्ग के लिए एक सार्वजनिक Attach()पद्धति और एक संरक्षित AttachWithoutReciprocating()पद्धति को जोड़ा जाए । बनाओ Aऔर Bपरस्पर मित्र बनाएं ताकि उनके Attach()तरीके दूसरे के फोन कर सकें AttachWithoutReciprocating():

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

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

यदि आप इसी तरह के तरीकों को लागू करते हैं B, तो आपको यह याद रखने की आवश्यकता नहीं होगी कि किस वर्ग को कॉल Attach()करना है।

मुझे यकीन है कि आप उस व्यवहार को एक MutuallyAttachableवर्ग में लपेट सकते हैं जो दोनों से Aऔर Bविरासत में मिला है, इस प्रकार अपने आप को दोहराने से बचें और जजमेंट डे पर बोनस अंक स्कोर करें। लेकिन यहां तक ​​कि गैर-परिष्कृत कार्यान्वयन-इट-इन-द-दोनों जगहों के दृष्टिकोण से काम हो जाएगा।


1
यह ठीक उसी तरह लगता है जैसे मैं अपने दिमाग के पीछे के समाधान में सोच रहा था (यानी ए और बी दोनों में अटैच करने के लिए)। मैं वास्तव में एक और अधिक DRY समाधान (मैं वास्तव में डुप्लिकेट कोड को नापसंद करता हूं) एक MutuallyAttachable वर्ग की तरह है कि आप इस व्यवहार की जरूरत है (जब भी मैं बहुत बार की जरूरत है) से प्राप्त कर सकते हैं पसंद है। बस किसी और द्वारा पेश किए गए एक ही समाधान को देखकर मुझे लगता है कि इसमें बहुत अधिक विश्वास है, धन्यवाद!
दिमित्री शूर्योव

इसे स्वीकार करना क्योंकि IMO यह अब तक का सबसे अच्छा समाधान है। धन्यवाद! मैं अब उस MutuallyAttachable वर्ग को लागू करने जा रहा हूं और यहां रिपोर्ट करता हूं कि अगर मैं किसी भी अनपेक्षित मुद्दे में चला जाता हूं (कुछ कठिनाइयां अब-एकाधिक विरासत के कारण उत्पन्न हो सकती हैं, जो मुझे अभी तक बहुत अनुभव नहीं है)।
दिमित्री शूर्योव

सब कुछ सुचारू रूप से काम किया। हालांकि, मूल प्रश्न पर मेरी पिछली टिप्पणी के अनुसार, मुझे एहसास होना शुरू हो रहा है कि मुझे मूल रूप से जो कल्पना की गई थी, उसके लिए मुझे इस कार्यक्षमता की आवश्यकता नहीं है। इन 2-वे कनेक्शनों पर नज़र रखने के बजाय, मैं बस उस फ़ंक्शन के पैरामीटर के रूप में प्रत्येक जोड़ी को पास करूंगा, जिसे इसकी आवश्यकता है। हालाँकि, यह बाद में भी काम आ सकता है।
दिमित्री शूर्योव

इस MutuallyAttachableवर्ग को मैंने इस कार्य के लिए लिखा है, यदि कोई इसका पुन: उपयोग करना चाहता है: goo.gl/VY9RB (Pastebin) संपादित करें: इस कोड को एक टेम्पलेट वर्ग बनाकर इसमें सुधार किया जा सकता है।
दिमित्री शूर्योव

1
मैंने MutuallyAttachable<T, U>Gist में क्लास का खाका रखा है । gist.github.com/3308058
दिमित्री शूर्योव

5

क्या रिश्ते के लिए अतिरिक्त गुण होना संभव है?

यदि ऐसा है, तो यह एक अलग वर्ग होना चाहिए।

यदि नहीं, तो किसी भी सूची-प्रबंधन तंत्र में पर्याप्तता होगी।


यदि यह एक अलग वर्ग है, तो A, B के अपने जुड़े हुए उदाहरणों को कैसे जान पाएगा और B, A के इसके जुड़े हुए उदाहरणों को जान सकेगा? उस समय, ए और बी दोनों को सी के साथ 1-टू-कई संबंध बनाने की आवश्यकता होगी, न?
दिमित्री शूर्योव

1
ए और बी दोनों को सी इंस्टेंस के संग्रह के लिए एक संदर्भ की आवश्यकता होगी; सी रिलेशनल मॉडलिंग में 'ब्रिज टेबल' के रूप में एक ही उद्देश्य पर काम करता है
स्टीवन ए लोवे

1

आमतौर पर जब मैं इस तरह की स्थितियों में आता हूं जो अजीब लगता है, लेकिन मैं यह नहीं बता सकता कि ऐसा क्यों है, क्योंकि मैं कुछ गलत कक्षा में डालने की कोशिश कर रहा हूं। लगभग हमेशा कार्यक्षमता को कक्षा से बाहर ले जाने Aऔर Bचीजों को अधिक स्पष्ट करने का एक तरीका है ।

एसोसिएशन को आगे बढ़ाने के लिए एक उम्मीदवार वह कोड है जो पहली बार में एसोसिएशन बनाता है। हो सकता है कि कॉल करने के बजाय attach()यह बस की एक सूची संग्रहीत करता है std::pair<A, B>। यदि संघ बनाने वाले कई स्थान हैं, तो इसे एक कक्षा में रखा जा सकता है।

इस कदम के लिए एक अन्य उम्मीदवार Zआपके मामले में संबंधित वस्तुओं का मालिक है ।

एसोसिएशन को आगे बढ़ाने के लिए एक और सामान्य उम्मीदवार वह कोड है जो अक्सर या वस्तुओं पर तरीकों को बुला रहा है । उदाहरण के लिए, कॉल करने के बजाय , जो आंतरिक रूप से सभी संबद्ध वस्तुओं के साथ कुछ करता है , यह पहले से ही प्रत्येक के लिए संघों और कॉल को जानता है । फिर, यदि कई स्थान इसे कहते हैं, तो इसे एक ही वर्ग में विभाजित किया जा सकता है।ABa->foo()Ba->foo(b)

बहुत बार ऑब्जेक्ट जो बनाते हैं, स्वयं बनाते हैं, और एसोसिएशन का उपयोग करते हैं, वही ऑब्जेक्ट होते हैं। जो कि रिफैक्टिंग को बहुत आसान बना सकता है।

अंतिम विधि अन्य संबंधों से संबंध प्राप्त करना है। उदाहरण के लिए, भाई-बहन कई-से-कई रिश्ते हैं, लेकिन भाई वर्ग में बहनों की सूची रखने और इसके विपरीत, आप उस रिश्ते को माता-पिता के रिश्ते से प्राप्त करते हैं। इस पद्धति का अन्य लाभ यह है कि यह सत्य का एक स्रोत बनाता है। भाई, बहन और माता-पिता की सूची के बीच विसंगति पैदा करने के लिए बग या रनटाइम त्रुटि के लिए कोई संभव तरीका नहीं है, क्योंकि आपके पास केवल एक सूची है।

इस तरह का रिफैक्टिंग एक जादुई फॉर्मूला नहीं है जो हर बार काम करता है। आपको अभी भी प्रयोग करना है जब तक कि आप प्रत्येक व्यक्तिगत परिस्थिति के लिए एक अच्छा फिट नहीं पाते हैं, लेकिन मैंने इसे लगभग 95% काम करने के लिए पाया है। शेष समय आपको बस अजीबोगरीब तरीके से जीना होगा।


0

यदि मैं इसे लागू कर रहा था, तो मैं अटैच विधि में ए और बी दोनों के अंदर अटैच डालूंगा, फिर मैं पास की गई वस्तु पर अटैच को बुलाऊंगा। इस तरह आप या तो A.Attach (B) या B.Attach (कॉल कर सकते हैं) ए)। मेरा वाक्य-विन्यास सही नहीं हो सकता है, मैंने वर्षों में c ++ का उपयोग नहीं किया है:

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);
    }
  }    
}

एक वैकल्पिक विधि 3 वर्ग, सी बनाने के लिए होगी जो एबी पेयरिंग की स्थिर सूची का प्रबंधन करती है।


AB जोड़े की सूची का प्रबंधन करने के लिए 3rd class C होने के आपके वैकल्पिक सुझाव के बारे में बस एक सवाल: A और B अपने जोड़ी सदस्यों को कैसे जानेंगे? क्या प्रत्येक ए और बी को (एकल) सी के लिए लिंक की आवश्यकता होगी, फिर सी को उचित जोड़े खोजने के लिए कहेंगे? B :: फू () {std :: वेक्टर <A *> जैसा = सी.गेट (यह); / * As As के साथ कुछ करें ... * /} या आपने कुछ और कल्पना की? क्योंकि यह भयानक रूप से दृढ़ लगता है।
दिमित्री शूर्योव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.