2012-05-11 21 views
5

में युग्मन और वैश्विक स्थिति को खत्म करना मैं एक गेम (अपने स्वयं के भौतिकी इंजन लिखना) पर काम कर रहा हूं, और इसे लिखने में अच्छी डिजाइन का पालन करने की कोशिश कर रहा हूं। हाल ही में, मैं कई हार्ड-टू-बग बग्स में चल रहा हूं, और इन बगों को आसान बनाने के लिए, मैं यूनिट-टेस्ट लिखने की कोशिश कर रहा हूं। ऐसा करना मुश्किल रहा है, इस तथ्य के कारण कि मेरे कई घटक कसकर युग्मित हैं, खासकर game मॉड्यूल के लिए।"गेम" सिंगलटन

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

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

मेरी इकाई ऑब्जेक्ट्स की स्थिति और वेग पाइथन गुण हैं जो वर्तमान समय के अनुसार गणना की जाती हैं। इसका मतलब यह है कि मैं इसे GameState ऑब्जेक्ट में फ़ंक्शन के रूप में पुनः लिखने के बिना बस पास नहीं कर सकता (जिसके परिणामस्वरूप icky वाक्यविन्यास)। कोड उदाहरण:

class Entity: 
    @property 
    def position(self): 
     """Interpolate the position from the last known position from that time.""" 
     # Here, the game class instance is referenced directly, creating coupling. 
     dt = game.game_time - self._last_valid_time 
     # x_f = x_i + v_i*t + 1/2 at^2 
     return self._position + self._velocity * dt + .5 * self._acceleration * dt**2 

मैं यह कैसे हल करने पर कुछ शोध किया है, और Dependency Injection भर में भाग गया। अनिवार्य रूप से, मैं प्रत्येक इकाई के प्रारंभकर्ता में गेमस्टेट 'गेटर' ऑब्जेक्ट पास कर सकता हूं। सभी गेमस्टेट 'गेटर' ऑब्जेक्ट्स बस वर्तमान स्थिति को वापस कर देंगे। उदाहरण GameStateGetter वर्ग:

class GameStateGetter: 
    _CurrentState = None 

    def update(self, new_state): 
     GameStateGetter._CurrentState = new_state 

    def __getattr__(self, name): 
     # GameState object properties are immutable, so this can't cause issues 
     return getattr(GameStateGetter._CurrentState, name) 
अब मेरे सवालों के लिए

  • गेमस्टेट 'गेटर' ऑब्जेक्ट्स का उपयोग करना भी एक अच्छा विचार होगा?

एक समस्या यह है वर्तमान खेल राज्य अद्यतन करने के लिए इंटरफेस होगा (मैं एक update विधि को परिभाषित किया है, लेकिन यह एक अजीब इंटरफ़ेस की तरह लगता है)। साथ ही, यह देखते हुए कि वैश्विक चर के साथ समस्या अप्रत्याशित प्रोग्राम स्थिति है, यह वास्तव में इसे रोक नहीं पाएगी।

  • वहाँ एक और तरीका है करने के लिए "इंजेक्षन" Entity वर्ग के position संपत्ति को game निर्भरता है?

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

किसी भी मदद के लिए अग्रिम धन्यवाद!

+1

अधिक आम शब्द है "प्रदाता।" (जो आपको गेटर विधियों के साथ भ्रमित प्रदाता वस्तुओं से भ्रमित करता है।) और हाँ, उनका उपयोग करना एक अच्छा विचार है। निर्भरता इंजेक्शन हिंसा की तरह है: यदि यह काम नहीं करता है, तो आप शायद इसके पर्याप्त उपयोग नहीं कर रहे हैं। –

+0

@RobertRossney धन्यवाद! निर्भरता इंजेक्शन की मेरी नई समझ अब ' प्रदाता' नामक कक्षाओं के साथ पहले देखे गए कुछ कोड से जुड़ती है। शायद कुछ अतिरिक्त जानकारी के साथ, इसे एक उत्तर के रूप में स्वीकार किया जा सकता है! – Darthfett

उत्तर

1

एक GameStateProvider में Entity निर्माता इंजेक्शन शुरू करने के लिए एक बहुत सरल है, और यह कुछ के रूप में अपने तर्क अधिक जटिल हो जाता पुनर्संशोधित जा सकता है कि आप के साथ छोड़ देता है।

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

पैटर्न इस तरह दिखता है:

class Entity(object): 

    def __init__(self, game_state_provider): 
     self.provider = game_state_provider 

    @property 
    def position(self): 
     # lazily evaluate position as a function of the current time 
     if self._last_valid_time == self.provider.current_time: 
     return self._position 
     self._last_valid_time = self.provider.current_time 
     self._position = // insert physics here 
     return self._position 
1

मुझे पहले प्रश्न के बारे में अनिश्चितता है, लेकिन यह थोड़ा सा दूर लाता है।

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

संपादित करें:

क्यों अपने खेल मॉड्यूल में मॉड्यूल स्तरीय विशेषता "GameState" नहीं बनाते हैं। आप इसे कहीं से भी एक्सेस कर सकते हैं। यदि आप GameState की प्रतियां चाहते हैं, तो क्लोन (या __deepcopy__) विधि को परिभाषित करें।

+0

मुझे लगता है कि इकाई परीक्षण के लिए काम कर सकता है, धन्यवाद! मेरे दूसरे प्रश्न के लिए - मैं वास्तव में 'एंटिटी' स्थिति संपत्ति को 'गेम' निर्भरता तक पहुंचने की अनुमति देने का जिक्र कर रहा था। मैं यह स्पष्ट करने के लिए संपादित कर दूंगा। – Darthfett