कम युग्मन और तंग सामंजस्य


11

बेशक यह स्थिति पर निर्भर करता है। लेकिन जब एक निचले स्तर की वस्तु या सिस्टम उच्च स्तर की प्रणाली के साथ संचार करता है, तो क्या कॉलबैक या घटनाओं को उच्चतर वस्तु के लिए सूचक रखने के लिए प्राथमिकता दी जानी चाहिए?

उदाहरण के लिए, हमारे पास एक worldवर्ग है जिसमें एक सदस्य चर है vector<monster> monsters। जब monsterकक्षा के साथ संचार होता है world, तो क्या मुझे कॉलबैक फ़ंक्शन का उपयोग करना पसंद करना चाहिए या फिर मुझे worldकक्षा के अंदर कक्षा के लिए एक संकेतक होना चाहिए monster?


प्रश्न शीर्षक में वर्तनी के अलावा, यह वास्तव में प्रश्न के रूप में प्रकाशित नहीं होता है। मुझे लगता है कि इसे फिर से परिभाषित करने से आप यहां जो पूछ रहे हैं, उसे ठोस बनाने में मदद मिल सकती है, क्योंकि मुझे नहीं लगता कि आपको इस सवाल का एक उपयोगी उत्तर अपने वर्तमान रूप में मिल सकता है। और यह गेम-डिज़ाइन का प्रश्न भी नहीं है, यह प्रोग्रामिंग संरचना पर एक प्रश्न है (निश्चित नहीं है कि इसका मतलब है कि डिज़ाइन टैग उपयुक्त है या उपयुक्त नहीं है, यह याद नहीं कर सकता है कि हम 'सॉफ़्टवेयर डिज़ाइन' और टैग्स पर कहाँ आए हैं)
MrCranky

जवाबों:


10

इसके तीन मुख्य तरीके हैं कि एक वर्ग दूसरे से बात करने के लिए कसकर युग्मित किए बिना कर सकता है:

  1. एक कॉलबैक फ़ंक्शन के माध्यम से।
  2. एक इवेंट सिस्टम के माध्यम से।
  3. एक इंटरफेस के माध्यम से।

तीनों एक-दूसरे से घनिष्ठ रूप से संबंधित हैं। कई मायनों में एक घटना प्रणाली सिर्फ कॉलबैक की एक सूची है। एक कॉलबैक एक ही विधि के साथ कमोबेश एक इंटरफ़ेस है।

C ++ में, मैं शायद ही कभी कॉलबैक का उपयोग करता हूं:

  1. C ++ में कॉलबैक के लिए अच्छा समर्थन नहीं है जो उनके thisपॉइंटर को बनाए रखता है , इसलिए ऑब्जेक्ट-ओरिएंटेड कोड में कॉलबैक का उपयोग करना मुश्किल है।

  2. कॉलबैक मूल रूप से एक गैर-एक्स्टेंसिबल एक-विधि इंटरफ़ेस है। समय के साथ, मुझे पता चलता है कि मैं लगभग हमेशा उस इंटरफ़ेस को परिभाषित करने के लिए एक से अधिक तरीकों की आवश्यकता को समाप्त करता हूं और एक एकल कॉलबैक शायद ही कभी पर्याप्त होता है।

इस मामले में, मैं शायद एक इंटरफ़ेस करूँगा। आपके प्रश्न में, आपको monsterवास्तव में यह बताने की ज़रूरत नहीं है कि वास्तव में संवाद करने की क्या आवश्यकता है world। एक अनुमान लगाते हुए, मैं कुछ ऐसा करूंगा:

class IWorld {
public:
  virtual Monster* getNearbyMonster(const Position & position) = 0;
  virtual Item*    getItemAt(const Position & position) = 0;
};

class Monster {
public:
  void update(IWorld * world) {
    // Do stuff...
  }
}

class World : public IWorld {
public:
  virtual Monster* getNearbyMonster(const Position & position) {
    // ...
  }

  virtual Item*    getItemAt(const Position & position) {
    // ...
  }

  // Lots of other stuff that Monster should not have access to...
}

यहाँ विचार यह है कि आप केवल IWorld(जो एक भद्दा नाम है) उस नंगे न्यूनतम पर डालें जिसे Monsterएक्सेस करने की आवश्यकता है। दुनिया के बारे में इसका दृष्टिकोण जितना संभव हो उतना संकीर्ण होना चाहिए।


1
+1 प्रतिनिधि (कॉलबैक) आमतौर पर समय बीतने के साथ और भी कई हो जाते हैं। राक्षसों को एक इंटरफ़ेस देना ताकि वे सामान प्राप्त कर सकें मेरी राय में जाने का एक अच्छा तरीका है।
माइकल कोलमैन

12

जिस फ़ंक्शन को आप कॉल कर रहे हैं उसे छिपाने के लिए कॉलबैक फ़ंक्शन का उपयोग न करें। यदि आपको कॉलबैक फ़ंक्शन में रखा गया था, और उस कॉलबैक फ़ंक्शन में एक और केवल एक फ़ंक्शन इसे सौंपा जाएगा, तो आप वास्तव में युग्मन को नहीं तोड़ रहे हैं। आप इसे अमूर्त की एक और परत के साथ मास्क कर रहे हैं। आप कुछ भी हासिल नहीं कर रहे हैं (शायद संकलन समय के अलावा), लेकिन आप स्पष्टता खो रहे हैं।

मैं वास्तव में इसे एक सर्वोत्तम अभ्यास नहीं कहूंगा, लेकिन यह एक सामान्य पैटर्न है कि ऐसी संस्थाएं हैं जो अपने माता-पिता के लिए एक संकेतक होने के लिए किसी चीज़ से निहित हैं।

कहा जा रहा है, यह आपके राक्षसों को दुनिया में कहे जाने वाले कार्यक्षमता का एक सीमित उपसमूह देने के लिए इंटरफ़ेस पैटर्न का उपयोग करते समय आपके लायक हो सकता है।


+1 माता-पिता को फोन करने के लिए राक्षस का एक सीमित तरीका देना मेरी राय में एक अच्छा मध्य तरीका है।
माइकल कोलमैन

7

आम तौर पर मैं द्विदिश लिंक से बचने की कोशिश करता हूं, लेकिन अगर मेरे पास उन्हें होना चाहिए, तो मैं पूरी तरह से सुनिश्चित करता हूं कि उन्हें बनाने के लिए एक तरीका है और उन्हें तोड़ने के लिए, ताकि आपको कभी भी विसंगतियां न हों।

अक्सर आप डेटा में पूरी तरह से बायडायरेक्शनल लिंक से बच सकते हैं क्योंकि यह आवश्यक है। एक तुच्छ परावर्तन इसे बनाना है ताकि दुनिया को एक कड़ी रखने वाले राक्षस होने के बजाय, आप उन राक्षस तरीकों के संदर्भ में दुनिया को पारित करें जिनकी आवश्यकता है। बेहतर अभी भी दुनिया के बिट्स के लिए केवल एक इंटरफेस में गुजरना है कि राक्षस को सख्ती से जरूरत है, जिसका अर्थ है कि राक्षस दुनिया के ठोस कार्यान्वयन पर भरोसा करने के लिए नहीं आता है। यह इंटरफ़ेस अलगाव सिद्धांत और निर्भरता उलटा सिद्धांत से मेल खाती है , लेकिन अतिरिक्त अमूर्तता का परिचय देना शुरू नहीं करता है जो आप कभी-कभी घटनाओं, संकेतों + स्लॉट, आदि के साथ प्राप्त कर सकते हैं।

एक तरह से, आप यह तर्क दे सकते हैं कि कॉलबैक का उपयोग करना एक बहुत ही विशिष्ट मिनी-इंटरफ़ेस है, और यह ठीक है। आपको यह तय करना होगा कि क्या आप इंटरफ़ेस ऑब्जेक्ट में एक संग्रह के माध्यम से अपने लक्ष्यों को अधिक सार्थक रूप से प्राप्त कर सकते हैं या विभिन्न कॉलबैक में कई मिश्रित हैं।


3

मैं इसमें निहित वस्तुओं को अपने कंटेनर को कॉल करने से बचने की कोशिश करता हूं क्योंकि मुझे लगता है कि इससे भ्रम पैदा होता है, इसे सही ठहराना बहुत आसान हो जाता है, यह अति प्रयोग हो जाएगा और यह निर्भरता बनाता है जिसे प्रबंधित नहीं किया जा सकता है।

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

आपके मामले में एक अन्य विकल्प, मैं शायद यह पता लगाऊंगा कि राक्षस वर्ग को विश्व स्तर के बारे में जानने की आवश्यकता क्यों है और आप सबसे अधिक संभावना पाएंगे कि विश्व वर्ग में कुछ ऐसा है जिसे अपने स्वयं के वर्ग में विभाजित किया जा सकता है जो इसे बनाता है राक्षस वर्ग के बारे में जानने के लिए।


2

आप घटनाओं के बिना, स्पष्ट रूप से बहुत दूर नहीं जाएंगे, लेकिन एक ईवेंट सिस्टम लिखना (और डिज़ाइन करना) शुरू करने से पहले, आपको वास्तविक सवाल पूछना चाहिए: राक्षस विश्व स्तर के साथ संवाद क्यों करेगा? क्या यह वास्तव में होना चाहिए?

चलो एक "शास्त्रीय" स्थिति लेते हैं, एक राक्षस एक खिलाड़ी पर हमला करता है।

राक्षस हमला कर रहा है: दुनिया बहुत अच्छी तरह से उस स्थिति की पहचान कर सकती है जहां एक नायक एक राक्षस के बगल में है, और राक्षस को हमला करने के लिए कहता है। तो राक्षस में समारोह होगा:

void Monster::attack(LivingCreature l)
{
  // Call to combat system
}

लेकिन दुनिया (जो पहले से ही राक्षस को जानता है) को राक्षस द्वारा जानने की आवश्यकता नहीं है। वास्तव में, राक्षस वर्ग विश्व के अस्तित्व को अनदेखा कर सकता है, जो शायद बेहतर है।

एक ही बात है जब राक्षस घूम रहा है (मैं उप-प्रणालियों को प्राणी को प्राप्त करने देता हूं और इसके लिए चाल की गणना / इरादा संभालता हूं, राक्षस सिर्फ डेटा का एक बैग है, लेकिन बहुत से लोग कहेंगे कि यह वास्तविक ओओपी नहीं है)।

मेरी बात: घटनाओं (या कॉलबैक) महान हैं, निश्चित रूप से, लेकिन वे आपकी हर समस्या का एकमात्र जवाब नहीं हैं।


1

जब भी मैं कर सकता हूं, मैं वस्तुओं के बीच संचार को अनुरोध-और-प्रतिक्रिया मॉडल में प्रतिबंधित करने का प्रयास करता हूं। मेरे कार्यक्रम में वस्तुओं पर एक आंशिक आंशिक आदेश है, जैसे कि किसी भी दो वस्तुओं के बीच A और B के बीच, A से सीधे या परोक्ष रूप से B की विधि या B के लिए, प्रत्यक्ष या अप्रत्यक्ष रूप से A की विधि को कॉल करने का एक तरीका हो सकता है। , लेकिन ए और बी के लिए एक-दूसरे के तरीकों को पारस्परिक रूप से कॉल करना संभव नहीं है। कभी-कभी, निश्चित रूप से, आप एक विधि के कॉलर से पिछड़े संचार करना चाहते हैं। कुछ तरीके हैं जो मुझे ऐसा करना पसंद है, और दोनों में से कोई कॉलबैक नहीं है।

एक तरीका विधि कॉल के रिटर्न मूल्य में अधिक जानकारी शामिल करना है, जिसका अर्थ है कि क्लाइंट कोड को यह तय करना होगा कि प्रक्रिया के नियंत्रण के बाद इसके साथ क्या करना है।

दूसरा तरीका यह है कि आप आपसी बच्चे को बुलाएं। यही है, अगर A, B को एक विधि कहता है, और B को A को कुछ सूचनाओं को संप्रेषित करने की आवश्यकता है, B, C पर एक विधि को कहता है, जहाँ A और B दोनों C को कॉल कर सकते हैं, लेकिन C A या B को कॉल नहीं कर सकता है। B के बाद C से सूचना प्राप्त करने के लिए जिम्मेदार है। A. ध्यान दें कि यह वास्तव में मेरे द्वारा प्रस्तावित पहले तरीके से मौलिक रूप से अलग नहीं है। ऑब्जेक्ट A अभी भी केवल रिटर्न मान से जानकारी प्राप्त कर सकता है; ऑब्जेक्ट ए में से कोई भी विधि B या C. द्वारा लागू नहीं की जाती है। इस ट्रिक की भिन्नता C को विधि के पैरामीटर के रूप में पास करना है, लेकिन C से A और B के संबंधों पर प्रतिबंध अभी भी लागू होता है।

अब, महत्वपूर्ण सवाल यह है कि मैं इस तरह से चीजें करने पर जोर क्यों देता हूं। इसके तीन मुख्य कारण हैं:

  • यह मेरी वस्तुओं को अधिक शिथिल रखता है। मेरी वस्तुएं अन्य वस्तुओं को एनकैप्सुलेट कर सकती हैं, लेकिन वे कभी भी कॉलर के संदर्भ पर निर्भर नहीं करेंगे, और संदर्भ कभी भी एन्कैप्सुलेटेड ऑब्जेक्ट्स पर निर्भर नहीं करेगा।
  • यह मेरे नियंत्रण प्रवाह को आसान बनाता है। यह मान लेना अच्छा है कि एकमात्र कोड जो आंतरिक selfविधि की स्थिति को बदल सकता है, जबकि एक विधि निष्पादित एक विधि और कोई अन्य नहीं है। यह उसी तरह का तर्क है जो समवर्ती वस्तुओं पर म्यूटेक्स लगाने के लिए नेतृत्व कर सकता है।
  • यह मेरी वस्तुओं के इनकैप्सुलेटेड डेटा पर आक्रमणकारियों को बचाता है। सार्वजनिक विधियों को आक्रमणकारियों पर निर्भर होने की अनुमति है, और उन आक्रमणकारियों का उल्लंघन किया जा सकता है यदि एक विधि को बाहरी रूप से कहा जा सकता है जबकि दूसरा पहले से ही निष्पादित हो रहा है।

मैं कॉलबैक के सभी उपयोगों के खिलाफ नहीं हूं। मेरी नीति के अनुसार "कॉल करने वाले को कभी नहीं" रखने पर, यदि कोई वस्तु B पर एक विधि का आह्वान करती है और उस पर कॉलबैक पास करती है, तो कॉलबैक A की आंतरिक स्थिति को नहीं बदल सकता है, और इसमें A द्वारा सम्मिलित की गई वस्तुएं भी शामिल हैं। A के संदर्भ में वस्तुएँ। दूसरे शब्दों में, कॉलबैक केवल बी द्वारा दी गई वस्तुओं पर तरीकों को लागू कर सकता है। कॉलबैक, वास्तव में उसी प्रतिबंध के तहत है जो बी है।

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


0

व्यक्तिगत रूप से? मैं सिर्फ एक सिंगलटन का उपयोग करता हूं।

हाँ, ठीक है, खराब डिज़ाइन, वस्तु-उन्मुख नहीं, आदि आप जानते हैं कि क्या? मुझे परवाह नहीं है । मैं एक खेल लिख रहा हूं, न कि तकनीक दिखाने का। कोई भी मुझे कोड पर ग्रेड करने वाला नहीं है। उद्देश्य एक ऐसा खेल बनाना है जो मज़ेदार हो, और मेरे रास्ते में आने वाली हर चीज़ का परिणाम एक ऐसा खेल हो, जो कम मज़ेदार हो।

क्या आपके पास कभी दो दुनियाएँ एक साथ चलेंगी? शायद! हो सकता है कि आप यह करें। लेकिन जब तक आप अभी उस स्थिति के बारे में नहीं सोच सकते, आप शायद नहीं करेंगे।

तो, मेरा समाधान: एक विश्व सिंगलटन बनाओ। उस पर कॉल फ़ंक्शन। पूरी गंदगी के साथ किया जाए। आप हर एक फ़ंक्शन के लिए एक अतिरिक्त पैरामीटर में पास हो सकते हैं - और कोई गलती नहीं करते हैं, यही वह जगह है जहां यह होता है। या आप बस उस कोड को लिख सकते हैं जो काम करता है।

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

और अगर आप तय करते हैं कि आप अब एकल नहीं चाहते हैं, तो आमतौर पर इससे छुटकारा पाना बहुत आसान है। कुछ काम लेता है, चारों ओर एक अरब पैरामीटर ले जाता है, लेकिन वे पैरामीटर हैं जिन्हें आपको वैसे भी पास करना होगा।


2
मैं शायद इसे थोड़ा अधिक संक्षिप्त रूप से उद्धृत करूंगा: "रिफैक्टर से डरो मत"।
टेट्राद

सिंगल्स दुष्ट हैं! ग्लोबल्स बहुत बेहतर हैं। आपके द्वारा सूचीबद्ध सभी सिंगलटन गुण एक वैश्विक के लिए समान हैं। मैं अपने विभिन्न उपतंत्रों के लिए वैश्विक संकेत (वास्तव में वैश्विक कार्य संदर्भों को लौटाने वाले) का उपयोग करता हूं और उन्हें अपने मुख्य कार्य में आरंभ / नष्ट करता हूं। ग्लोबल्स पॉइंटर्स इनिशियलाइज़ेशन ऑर्डर के मुद्दों से बचते हैं, आंसू गिराने के दौरान सिंगलटैंगल्स को झकझोरते हैं, सिंगलटॉन के गैर-तुच्छ निर्माण आदि को दोहराते हैं।
deft_code

@ टेट्राद, अत्यधिक सहमत। यह आपके पास सबसे अच्छे कौशल में से एक है।
ज़ोरबहुत
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.