जब कई वर्गों को समान डेटा का उपयोग करने की आवश्यकता होती है, तो डेटा कहां घोषित किया जाना चाहिए?


39

मेरे पास C ++ में एक बुनियादी 2 डी टॉवर रक्षा खेल है।

प्रत्येक नक्शा एक अलग वर्ग है जो गेमस्टेट से विरासत में मिला है। नक्शा खेल में प्रत्येक वस्तु के लिए तर्क और ड्राइंग कोड को दर्शाता है और मानचित्र पथ जैसे डेटा सेट करता है। छद्म कोड में तर्क खंड कुछ इस तरह दिख सकता है:

update():
  for each creep in creeps:
    creep.update()
  for each tower in towers:
    tower.update()
  for each missile in missiles:
    missile.update()

वस्तुओं (ढोंगी, टॉवर और मिसाइल) को वेक्टर-ऑफ-पॉइंटर्स में संग्रहीत किया जाता है। टावरों के पास नई मिसाइल बनाने और लक्ष्यों की पहचान करने के लिए वेक्टर-ऑफ-क्रीप्स और वेक्टर-ऑफ़-मिसाइलों तक पहुंच होनी चाहिए।

सवाल यह है: मैं वैक्टर कहां घोषित करूं? क्या उन्हें मानचित्र वर्ग का सदस्य होना चाहिए, और टॉवर के लिए तर्क के रूप में पारित किया जाना चाहिए। () फ़ंक्शन? या विश्व स्तर पर घोषित किया गया? या वहाँ अन्य समाधान मैं पूरी तरह से याद कर रहा हूँ?

जब कई वर्गों को समान डेटा का उपयोग करने की आवश्यकता होती है, तो डेटा कहां घोषित किया जाना चाहिए?


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

-1 यदि यह गेम प्रोग्रामिंग से संबंधित है, तो पिज्जा खाना भी है। अपने आप को कुछ अच्छे सॉफ्टवेयर डिजाइन की किताबें पकड़ो
Maik Semder

9
@ माईक: सॉफ्टवेयर प्रोग्रामिंग गेम प्रोग्रामिंग से कैसे संबंधित है? सिर्फ इसलिए कि यह प्रोग्रामिंग के अन्य क्षेत्रों पर भी लागू होता है, यह इसे ऑफ-टॉपिक नहीं बनाता है।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

सॉफ्टवेयर डिजाइन पैटर्न की सूची @BlueRaja SO पर बेहतर अनुकूल है, यही कारण है कि यह सब के बाद के लिए है। GD.SE गेम प्रोग्रामिंग के लिए है, न कि सॉफ्टवेयर डिज़ाइन के लिए
Maik Semder

जवाबों:


53

जब आपको अपने पूरे कार्यक्रम में कक्षा के एक ही उदाहरण की आवश्यकता होती है, तो हम उस वर्ग को सेवा कहते हैं । कार्यक्रमों में सेवाओं को लागू करने के कई मानक तरीके हैं:

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

    class MyClass
    {
    private:
        static MyClass* _instance;
        MyClass() {} //private constructor
    
    public:
        static MyClass* getInstance();
        void method();
    };
    
    ...
    
    MyClass* MyClass::_instance = NULL;
    MyClass* MyClass::getInstance()
    {
        if(_instance == NULL)
            _instance = new MyClass(); //Not thread-safe version
        return _instance;
    
        //Note that _instance is *never* deleted - 
        //it exists for the entire lifetime of the program!
    }
    
  • निर्भरता इंजेक्शन (DI) । यह सिर्फ एक निर्माता पैरामीटर के रूप में सेवा पास करने का मतलब है। एक सेवा को कक्षा में पास करने के लिए पहले से ही मौजूद होना चाहिए, इसलिए दो सेवाओं के लिए एक दूसरे पर भरोसा करने का कोई तरीका नहीं है; 98% मामलों में, यह वही है जो आप चाहते हैं (और अन्य 2% के लिए, आप हमेशा एक setWhatever()विधि बना सकते हैं और सेवा में पास हो सकते हैं ) । इस वजह से, DI के पास अन्य विकल्पों की तरह ही युग्मन समस्याएं नहीं हैं। इसका उपयोग मल्टीथ्रेडिंग के साथ किया जा सकता है, क्योंकि प्रत्येक थ्रेड में बस हर सेवा का अपना उदाहरण हो सकता है (और केवल उन लोगों को साझा करता है जो इसे पूरी तरह से आवश्यक हैं)। यदि आप इस बारे में परवाह करते हैं तो यह कोड यूनिट-टेस्ट करने योग्य भी बनाता है।

    निर्भरता इंजेक्शन के साथ समस्या यह है कि यह अधिक मेमोरी लेता है; अब हर वर्ग के प्रत्येक उदाहरण के लिए हर उस सेवा के संदर्भों की आवश्यकता होगी जिसका वह उपयोग करेगा। इसके अलावा, जब आपको बहुत अधिक सेवाएँ होती हैं, तो इसका उपयोग करने पर गुस्सा आता है; ऐसी रूपरेखाएँ हैं जो अन्य भाषाओं में इस समस्या को कम करती हैं, लेकिन C ++ के परावर्तन में कमी के कारण, C ++ में DI चौखटे केवल मैन्युअल रूप से करने की तुलना में और भी अधिक काम करते हैं।

    //Example of dependency injection
    class Tower
    {
    private:
        MissileCreationService* _missileCreator;
        CreepLocatorService* _creepLocator;
    public:
        Tower(MissileCreationService*, CreepLocatorService*);
    }
    
    //In order to create a tower, the creating-class must also have instances of
    // MissileCreationService and CreepLocatorService; thus, if we want to 
    // add a new service to the Tower constructor, we must add it to the
    // constructor of every class which creates a Tower as well!
    //This is not a problem in languages like C# and Java, where you can use
    // a framework to create an instance and inject automatically.
    

    देखें यह पेज एक और उदाहरण के लिए (Ninject, एक सी # डि ढांचे के लिए प्रलेखन से)।

    निर्भरता इंजेक्शन इस समस्या का सामान्य समाधान है, और इसका उत्तर है कि आप StackOverflow.com पर इस तरह के प्रश्नों के लिए सबसे अधिक उत्थान पाएंगे। DI एक प्रकार का व्युत्क्रम नियंत्रण (IoC) है।

  • सेवा लोकेटर । मूल रूप से, सिर्फ एक वर्ग जो हर सेवा का एक उदाहरण रखता है। आप इसे प्रतिबिंब का उपयोग करके कर सकते हैं , या आप हर बार एक नया उदाहरण जोड़ सकते हैं जब आप एक नई सेवा बनाना चाहते हैं। आपके पास पहले जैसी ही समस्या है - इस लोकेटर तक कक्षाएं कैसे पहुंचती हैं? - जो उपरोक्त किसी भी तरीके से हल किया जा सकता है, लेकिन अब आपको केवल ServiceLocatorदर्जनों सेवाओं के बजाय इसे अपनी कक्षा के लिए करना होगा । यह विधि इकाई-परीक्षण योग्य भी है, यदि आप उस प्रकार की परवाह करते हैं।

    सेवा लोकेटर उलटा नियंत्रण (IoC) का एक और रूप है। आमतौर पर, स्वचालित निर्भरता इंजेक्शन करने वाले ढांचे में एक सेवा लोकेटर भी होगा।

    XNA (Microsoft का C # गेम प्रोग्रामिंग फ्रेमवर्क) में एक सेवा लोकेटर शामिल है; इसके बारे में अधिक जानने के लिए, इस उत्तर को देखें ।


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


टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
जोश

सबसे अच्छे, स्पष्ट उत्तरों में से एक जो मैंने कभी पढ़ा है। बहुत बढ़िया। मैंने सोचा था कि एक सेवा को हमेशा साझा किया जाना चाहिए था।
निकोस

5

मैं व्यक्तिगत रूप से यहां बहुरूपता का उपयोग करूंगा। एक missileवेक्टर, एक towerवेक्टर और एक वेक्टर क्यों creepहै..जब वे सभी एक ही फ़ंक्शन को कॉल करते हैं; update? क्यों नहीं कुछ बेस क्लास Entityया बिंदुओं के वेक्टर होते हैं GameObject?

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

इसके चारों ओर एक तरह से संदेश प्रणाली है। towerके लिए एक संदेश भेज सकते हैं map(इसके मालिक को जो इसे तक पहुँच गया है, शायद एक संदर्भ?) है कि यह एक हिट creepहै, और mapउसके बाद कहता है creepयह हिट हो गया है। यह बहुत साफ है और डेटा को अलग करता है।

एक और तरीका यह है कि आप जो चाहते हैं, उसके लिए केवल मानचित्र को ही खोजें। हालाँकि, यहाँ अद्यतन आदेश के साथ समस्याएँ हो सकती हैं।


1
बहुरूपता के बारे में आपका सुझाव वास्तव में प्रासंगिक नहीं है। मैंने उन्हें अलग-अलग वैक्टर में संग्रहीत किया है, इसलिए मैं प्रत्येक प्रकार पर व्यक्तिगत रूप से पुनरावृत्ति कर सकता हूं, जैसे कि ड्राइंग कोड (जहां मैं कुछ वस्तुओं को पहले खींचना चाहूंगा) या टक्कर कोड में।
जूसी २

मेरे उद्देश्यों के लिए मानचित्र संस्थाओं का मालिक है, क्योंकि यहाँ मानचित्र 'स्तर' के अनुरूप है। मैं आपके विचारों को संदेशों के बारे में विचार करूंगा, धन्यवाद।
रसदार

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

0

यह एक ऐसा मामला है जिसमें सख्त ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग (OOP) टूट जाती है।

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

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

इस दृष्टिकोण का एक चरम उदाहरण इकाई प्रणाली वास्तुकला है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.