OOP में परिपत्र संदर्भों की आवश्यकता प्रतीत होने वाली इस वास्तविक दुनिया गतिविधि को मॉडल करने का उचित तरीका क्या है?


24

मैं परिपत्र संदर्भ के बारे में जावा परियोजना में एक समस्या के साथ कुश्ती कर रहा हूं। मैं एक वास्तविक दुनिया की स्थिति को मॉडल करने की कोशिश कर रहा हूं जिसमें ऐसा लगता है कि प्रश्न में वस्तुएं अन्योन्याश्रित हैं और एक दूसरे के बारे में जानने की जरूरत है।

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

कक्षाएं हैं:

  • RuleBook: एक वस्तु जिसे बोर्ड के प्रारंभिक लेआउट के रूप में इस तरह की चीजों के लिए पूछताछ की जा सकती है, अन्य प्रारंभिक गेम राज्य की जानकारी जैसे कि पहले कौन ले जाता है, जो मूव उपलब्ध है, प्रस्तावित मूव के बाद गेम स्टेट का क्या होता है, और एक मूल्यांकन एक वर्तमान या प्रस्तावित बोर्ड की स्थिति।

  • बोर्ड: एक गेम बोर्ड का एक सरल प्रतिनिधित्व, जिसे एक चाल को प्रतिबिंबित करने का निर्देश दिया जा सकता है।

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

  • चाल: एक एकल चाल। इसमें परमाणुओं की एक सूची के रूप में इस कदम के बारे में सब कुछ शामिल है: यहां से एक टुकड़ा उठाएं, इसे नीचे रखें, वहां से कब्जा कर लिया गया टुकड़ा हटा दें।

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

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

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

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

आपके द्वारा दी जा सकने वाली कोई भी सहायता के लिए शुक्रिया!


7
क्या होगा यदि RuleBookउदाहरण के लिए Stateएक तर्क के रूप में लिया गया , और वैध लौटा MoveList, यानी "यहाँ हम अभी क्या हैं, आगे क्या किया जा सकता है?"
जोंशरशिप

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

4
देव वस्तु से बचना (BigClassThatDoesAlmostEverythingInTheGame) परिपत्र संदर्भों से बचने की तुलना में बहुत अधिक महत्वपूर्ण है।
user281377

2
@ user281377 वे जरूरी परस्पर अनन्य उद्देश्य नहीं हैं, हालांकि!
जोंसरशिप

1
क्या आप मॉडलिंग के प्रयास दिखा सकते हैं? उदाहरण के लिए एक आरेख?
उपयोगकर्ता

जवाबों:


47

मैं परिपत्र संदर्भ के बारे में जावा परियोजना में एक समस्या के साथ कुश्ती कर रहा हूं।

जावा का कचरा संग्राहक संदर्भ गिनती तकनीकों पर निर्भर नहीं करता है। परिपत्र संदर्भ जावा में किसी भी प्रकार की समस्या पैदा नहीं करते हैं। जावा में पूरी तरह से प्राकृतिक परिपत्र संदर्भों को नष्ट करने में समय व्यतीत होता है।

मैंने इसे कोडित कर दिया [...] लेकिन समस्या यह है कि यह परिपत्र संदर्भों से भरा है। मैंने इसे एक स्रोत फ़ाइल में सभी अंतःनिर्मित कक्षाओं को भरकर वापस लागू किया , [...]

आवश्यक नहीं। यदि आप बस एक ही बार में सभी स्रोत फ़ाइलों को संकलित करते हैं (उदाहरण के लिए javac *.java), तो कंपाइलर समस्याओं के बिना सभी आगे के संदर्भों को हल करेगा।

या क्या मुझे अन्योन्याश्रित वर्गों के साथ रहना चाहिए और संकलक को किसी भी तरह से संकलित करना चाहिए, [...]

हाँ। आवेदन कक्षाएं अन्योन्याश्रित होने की उम्मीद है। सभी जावा स्रोत फ़ाइलों को संकलित करना जो एक ही पैकेज में एक ही बार में होशियार हैक नहीं है, यह ठीक उसी तरह है जैसे जावा काम करने वाला है।


24
"परिपत्र संदर्भ जावा में किसी भी तरह की समस्या पैदा नहीं करते हैं।" संकलन के संदर्भ में, यह सच है। परिपत्र संदर्भों को बुरा डिजाइन माना जाता है , हालांकि।
Chop

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

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

3
कृपया असंबंधित प्रोग्रामिंग भाषाओं के बारे में असंतुष्ट FUD न फैलाएं। पाइथन ने उम्र से चक्र के संदर्भ चक्रों का समर्थन किया है ( डॉक्स , एसओ पर: यहां और यहां )।
क्रिश्चियन आइचिंगर

2
IMHO यह उत्तर केवल औसत दर्जे का है, क्योंकि ओपी के उदाहरण के लिए उपयोगी होने वाले गोलाकार संदर्भों के बारे में एक शब्द भी नहीं है।
डॉक ब्राउन

22

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

वास्तव में, बहुत कम परिदृश्य हैं जहां जावा संकलक एक परिपत्र निर्भरता से इनकार करेगा। (नोट: और भी हो सकता है, मैं केवल अभी निम्नलिखित के बारे में सोच सकता हूं।)

  1. इनहेरिटेंस में: आपके पास क्लास ए का विस्तार क्लास बी नहीं हो सकता है जो बदले में क्लास ए का विस्तार करता है, और यह पूरी तरह से उचित है कि आपके पास यह नहीं हो सकता है, क्योंकि वैकल्पिक तार्किक दृष्टिकोण से बिल्कुल कोई मतलब नहीं होगा।

  2. विधि-स्थानीय कक्षाओं के बीच: एक विधि के भीतर घोषित की गई कक्षाएं एक दूसरे का संदर्भ नहीं दे सकती हैं। यह संभवत: जावा कंपाइलर की एक सीमा के अलावा और कुछ नहीं है, संभवतः क्योंकि ऐसा करने की क्षमता अतिरिक्त जटिलता को सही ठहराने के लिए पर्याप्त उपयोगी नहीं है कि इसे समर्थन करने के लिए संकलक में जाना होगा। (अधिकांश जावा प्रोग्रामर इस तथ्य के बारे में भी नहीं जानते हैं कि आप एक विधि के भीतर एक वर्ग की घोषणा कर सकते हैं, अकेले कई वर्गों की घोषणा कर सकते हैं, और फिर इन कक्षाओं को एक दूसरे का संदर्भ दें।)

इसलिए, यह महसूस करना और इसे बाहर निकालना महत्वपूर्ण है कि परिपत्र निर्भरता को कम करने की खोज डिजाइन शुद्धता के लिए एक खोज है, न कि तकनीकी शुद्धता की खोज।

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

जिस विशेष स्थिति में आपके हाथ में है, यह मुझे लगता है कि आपको एक नई इकाई की आवश्यकता है, जिसे शायद "गेम" या "गेमलॉजिक" कहा जाता है, जो अन्य सभी संस्थाओं को जानता है, (बिना किसी अन्य संस्था को जाने, ) ताकि अन्य संस्थाओं को एक दूसरे को जानना न पड़े।

उदाहरण के लिए, यह मुझे अनुचित लगता है कि आपकी RuleBook इकाई को GameState संस्था के बारे में कुछ भी जानने की आवश्यकता है, क्योंकि एक नियम पुस्तिका ऐसी चीज है जिसे हम खेलने के लिए परामर्श देते हैं, यह ऐसा कुछ नहीं है जो खेलने में सक्रिय भाग लेता है। तो, यह यह नई "गेम" इकाई है जिसे नियम बुक और गेम स्टेट दोनों से परामर्श करने की आवश्यकता है ताकि यह निर्धारित किया जा सके कि क्या उपलब्ध हैं, और यह परिपत्र निर्भरता को समाप्त करता है।

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


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

10

गेम थ्योरी खेलों को पिछली चालों की सूची के रूप में मानती है (मूल्य प्रकार जिसमें उन्हें खेला गया था) और एक फ़ंक्शन ValidMoves (पिछले मेव्स)

मैं खेल के गैर यूआई भाग के लिए इस पैटर्न का प्रयास और पालन करूंगा और बोर्ड सेटअप जैसी चीजों को चाल के रूप में समझूंगा।

यूआई तब तर्क के लिए एक तरह से मानक ओ ओ सामान हो सकता है


सघन टिप्पणियों के लिए अद्यतन करें

शतरंज पर विचार करें। शतरंज के खेल को आमतौर पर चालों की सूची के रूप में दर्ज किया जाता है। http://en.wikipedia.org/wiki/Portable_Game_Notation

चाल की सूची बोर्ड की एक तस्वीर की तुलना में खेल की पूरी स्थिति को बेहतर ढंग से परिभाषित करती है।

उदाहरण के लिए कहें कि हम बोर्ड, टुकड़ा, चाल आदि के लिए ऑब्जेक्ट बनाना शुरू करते हैं और Methods जैसे Piece.GetValidMoves ()

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

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

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

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

यदि हम सेटअप मूव्स को मूव्स के रूप में, 'बॉक्स से स्क्वायर X तक' जोड़ते हैं और उस मूव को समझने के लिए रूल्स ऑब्जेक्ट को एडेप्ट करते हैं, तो हम फिर भी मूव्स के सीक्वेंस के रूप में गेम का प्रतिनिधित्व कर सकते हैं।

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


यह ValidMoves के कार्यान्वयन को जटिल करेगा, जो आपके तर्क को धीमा कर देगा।
तैमूर

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

1
झंडे और सामान जोड़ना वह जटिलता है जिसे आप केवल इतिहास ले जाने से बचाते हैं। वर्तमान बोर्ड सेटअप को प्राप्त करने के लिए 100 शतरंज चालों को कहना महंगा नहीं है और आप चालों के बीच परिणाम को कैश कर सकते हैं
इवान

1
आप नियमों को प्रतिबिंबित करने के लिए अपने ऑब्जेक्ट मॉडल को बदलने से भी बचते हैं। यानी शतरंज के लिए, यदि आप वैधता बनाते हैं -> टुकड़ा + बोर्ड, तो आप असफलता, एन-निष्क्रिय, पहले पंजे और टुकड़े को बढ़ावा देने के लिए आगे बढ़ते हैं और वस्तुओं में अतिरिक्त जानकारी जोड़ते हैं या एक तीसरी वस्तु को संदर्भित करते हैं। आप यह भी जान सकते हैं कि यह कौन है और अवधारणाएं जैसे कि खोजा गया चेक
ईवान

1
@ गैब सभी boardLayoutका एक कार्य है priorMoves(अर्थात यदि हमने इसे राज्य के रूप में बनाए रखा, तो प्रत्येक के अलावा कुछ भी योगदान नहीं दिया जाएगा thisMove)। इसलिए इवान का सुझाव अनिवार्य रूप से "बीच के आदमी को काटना" है - मान्य इसके बजाय सभी पूर्व का एक सीधा कार्य करता है validMoves( boardLayout( priorMoves ) )
OJFord

8

ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में दो वर्गों के बीच एक परिपत्र संदर्भ को हटाने का मानक तरीका एक इंटरफ़ेस पेश करना है जो तब उनमें से एक द्वारा कार्यान्वित किया जा सकता है। तो आपके मामले में, आप कर सकते थेRuleBook जिक्रState फिर एक को संदर्भित करता है InitialPositionProvider(जो एक इंटरफ़ेस द्वारा कार्यान्वित किया जाएगा RuleBook)। इससे परीक्षण करना भी आसान हो जाता है, क्योंकि आप Stateपरीक्षण उद्देश्यों के लिए एक अलग (संभवत: सरल) प्रारंभिक स्थिति का उपयोग कर सकते हैं।


6

मेरा मानना ​​है कि परिपत्र संदर्भ और आपके मामले में देव वस्तु को खेल के राज्य और नियम मॉडल से खेल के प्रवाह के नियंत्रण को अलग करके आसानी से हटाया जा सकता है। ऐसा करने से आप संभवतः बहुत अधिक लचीलापन प्राप्त करेंगे और कुछ अनावश्यक जटिलता से छुटकारा पा सकते हैं।

मुझे लगता है कि आपके पास एक नियंत्रक ("एक गेम मास्टर" यदि आप चाहें तो) जो खेल के प्रवाह को नियंत्रित करता है और नियम पुस्तिका या खेल राज्य को यह जिम्मेदारी देने के बजाय वास्तविक राज्य परिवर्तनों को संभालता है।

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

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

कंट्रोलर को गेम स्टेट्स और रूल बुक और शायद गेम मॉडल की कुछ अन्य वस्तुओं के बारे में पता होना चाहिए, लेकिन इसे विवरण के साथ गड़बड़ करने की आवश्यकता नहीं होनी चाहिए।


4
बिल्कुल मेरी सोच। ओपी एक ही कक्षाओं में बहुत अधिक डेटा और प्रक्रिया को मिला रहा है । बेहतर होगा कि इन्हें और अधिक विभाजित किया जाए। यह इस विषय पर एक अच्छी बात है। Btw, जब मैं "गेम स्टेट के लिए दृश्य" पढ़ता हूं, तो मुझे लगता है कि "फ़ंक्शन के लिए तर्क।" +100 अगर मैं कर सका।
jpmc26

5

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

आइए प्रत्येक वर्ग क्या करता है इसका वर्णन करने के साथ शुरू करें।

GameStateवर्ग केवल खेल की वर्तमान स्थिति के बारे में जानकारी शामिल करना चाहिए। इसमें इस बारे में कोई जानकारी नहीं होनी चाहिए कि खेल की पिछली अवस्थाएँ या भविष्य की चालें क्या संभव हैं। इसमें केवल इस बारे में जानकारी होनी चाहिए कि शतरंज में कौन से टुकड़े हैं, या बैकगैमौन में कितने और किस प्रकार के चेकर्स हैं। GameStateशतरंज में या बैकगैमौन में दोहरीकरण घन के बारे में कैसलिंग बारे में जानकारी, कुछ अतिरिक्त जानकारी शामिल करने के लिए होगा।

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

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

आप GameHistoryउन सभी चालों पर नज़र रखने के लिए एक वर्ग भी बना सकते हैं जो सभी किए गए हैं और GameStatesजो कुछ हुआ है। एक नया वर्ग आवश्यक है क्योंकि हमने तय किया कि इससे पहले आने वाले GameStateसभी GameStateएस को जानने के लिए एक जिम्मेदार नहीं होना चाहिए ।

यह उन कक्षाओं / इंटरफेस को समाप्त करता है जिन पर मैं चर्चा करूंगा। आपकी भी Boardक्लास है। लेकिन मुझे लगता है कि अलग-अलग खेलों में बोर्ड काफी अलग हैं कि यह देखना मुश्किल है कि बोर्डों के साथ उदारता से क्या किया जा सकता है। अब मैं जेनेरिक इंटरफेस देने और जेनेरिक कक्षाओं को लागू करने के लिए जाऊंगा।

सबसे पहले है GameState। चूंकि यह वर्ग पूरी तरह से विशेष खेल पर निर्भर है, इसलिए कोई सामान्य Gamestateइंटरफ़ेस या वर्ग नहीं है।

अगला है Move। जैसा कि मैंने कहा, यह एक ऐसे इंटरफ़ेस के साथ दर्शाया जा सकता है जिसमें एक एकल विधि है जो एक पूर्व-चाल राज्य को ले जाती है और एक बाद की चाल की स्थिति पैदा करती है। यहाँ इस इंटरफ़ेस के लिए कोड है:

package boardgame;

/**
 *
 * @param <T> The type of GameState
 */
public interface Move<T> {

    T makeResultingState(T preMoveState) throws IllegalArgumentException;

}

ध्यान दें कि एक प्रकार का पैरामीटर है। यह इसलिए है, उदाहरण के लिए, ChessMoveपूर्व-चाल के विवरण के बारे में जानने की आवश्यकता होगी ChessGameState। इसलिए, उदाहरण के लिए, वर्ग की घोषणा ChessMoveहोगी

class ChessMove extends Move<ChessGameState>,

जहाँ आपने पहले से ही एक ChessGameStateवर्ग को परिभाषित किया होगा ।

आगे मैं सामान्य RuleBookवर्ग पर चर्चा करूंगा । यहाँ कोड है:

package boardgame;

import java.util.List;

/**
 *
 * @param <T> The type of GameState
 */
public interface RuleBook<T> {

    T makeInitialState();

    List<Move<T>> makeMoveList(T gameState);

    StateEvaluation evaluateState(T gameState);

    boolean isMoveLegal(Move<T> move, T currentState);

}

फिर से GameStateकक्षा के लिए एक प्रकार का पैरामीटर है । चूंकि यह RuleBookजानना चाहिए कि प्रारंभिक अवस्था क्या है, इसलिए हमने प्रारंभिक अवस्था देने के लिए एक विधि रखी है। चूंकि RuleBookमाना जाता है कि कौन सी चालें कानूनी हैं, हमारे पास यह परीक्षण करने के तरीके हैं कि क्या किसी दिए गए राज्य में एक चाल कानूनी है और किसी दिए गए राज्य के लिए कानूनी चालों की एक सूची देने के लिए। अंत में, मूल्यांकन करने के लिए एक विधि है GameState। सूचना RuleBookकेवल यह बताने के लिए ज़िम्मेदार होनी चाहिए कि क्या एक या दूसरे खिलाड़ी पहले ही जीत चुके हैं, लेकिन ऐसा नहीं है जो खेल के बीच में बेहतर स्थिति में है। यह तय करना कि कौन बेहतर स्थिति में है, एक जटिल चीज है जिसे अपनी कक्षा में स्थानांतरित किया जाना चाहिए। इसलिए StateEvaluationवर्ग वास्तव में केवल एक सरल एनम निम्नानुसार है:

package boardgame;

/**
 *
 */
public enum StateEvaluation {

    UNFINISHED,
    PLAYER_ONE_WINS,
    PLAYER_TWO_WINS,
    DRAW,
    ILLEGAL_STATE
}

अन्त में, GameHistoryकक्षा का वर्णन करते हैं । यह वर्ग उन सभी पदों को याद रखने के लिए ज़िम्मेदार है जो खेल में पहुँच गए थे और साथ ही चालें भी खेली गई थीं। मुख्य बात यह करने में सक्षम होना चाहिए एक के Moveरूप में खेला रिकॉर्ड है । आप पूर्ववत् Moves के लिए कार्यक्षमता भी जोड़ सकते हैं । मेरे पास एक कार्यान्वयन है।

package boardgame;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @param <T> The type of GameState
 */
public class GameHistory<T> {

    private List<T> states;
    private List<Move<T>> moves;

    public GameHistory(T initialState) {
        states = new ArrayList<>();
        states.add(initialState);
        moves = new ArrayList<>();
    }

    void recordMove(Move<T> move) throws IllegalArgumentException {
        moves.add(move);
        states.add(move.makeResultingState(getMostRecentState()));
    }

    void resetToNthState(int n) {
        states = states.subList(0, n + 1);
        moves = moves.subList(0, n);
    }

    void undoLastMove() {
        resetToNthState(getNumberOfMoves() - 1);
    }

    T getMostRecentState() {
        return states.get(getNumberOfMoves());
    }

    T getStateAfterNthMove(int n) {
        return states.get(n + 1);
    }

    Move<T> getNthMove(int n) {
        return moves.get(n);
    }

    int getNumberOfMoves() {
        return moves.size();
    }

}

अंत में, हम एक Gameसाथ सब कुछ बाँधने के लिए एक वर्ग बनाने की कल्पना कर सकते थे। यह Gameवर्ग उन तरीकों को उजागर करने वाला है जो लोगों के लिए यह देखना संभव बनाता है कि वर्तमान क्या GameStateहै, यह देखें कि यदि किसी के पास एक है, तो देखें कि कौन सी चाल चल सकते हैं, और एक चाल खेल सकते हैं। मेरे पास एक कार्यान्वयन है

package boardgame;

import java.util.List;

/**
 *
 * @author brian
 * @param <T> The type of GameState
 */
public class Game<T> {

    GameHistory<T> gameHistory;
    RuleBook<T> ruleBook;

    public Game(RuleBook<T> ruleBook) {
        this.ruleBook = ruleBook;
        final T initialState = ruleBook.makeInitialState();
        gameHistory = new GameHistory<>(initialState);
    }

    T getCurrentState() {
        return gameHistory.getMostRecentState();
    }

    List<Move<T>> getLegalMoves() {
        return ruleBook.makeMoveList(getCurrentState());
    }

    void doMove(Move<T> move) throws IllegalArgumentException {
        if (!ruleBook.isMoveLegal(move, getCurrentState())) {
            throw new IllegalArgumentException("Move is not legal in this position");
        }
        gameHistory.recordMove(move);
    }

    void undoMove() {
        gameHistory.undoLastMove();
    }

    StateEvaluation evaluateState() {
        return ruleBook.evaluateState(getCurrentState());
    }

}

इस वर्ग में देखें कि RuleBookवर्तमान क्या है, यह जानने के लिए जिम्मेदार नहीं GameStateहै। यही GameHistoryकाम है। तो यह Gameपूछता है GameHistoryकि वर्तमान स्थिति क्या है और यह जानकारी यह बताने के लिए देता है कि कानूनी कदम क्या हैं या यदि कोई जीता है, तो यह कहने RuleBookकी Gameआवश्यकता है।

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


3

मेरे अनुभव में, परिपत्र संदर्भ आम तौर पर इंगित करते हैं कि आपका डिज़ाइन अच्छी तरह से सोचा-समझा नहीं है।

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


+1। आप एक भौतिक बोर्ड गेम खरीदते हैं, आपको एक नियम पुस्तिका मिलती है जो बिना राज्य के नियमों का वर्णन करने में सक्षम है।
unperson325680

1

परिपत्र निर्भरता जरूरी एक तकनीकी समस्या नहीं है, लेकिन इसे एक कोड गंध माना जाना चाहिए, जो आमतौर पर एकल जिम्मेदारी सिद्धांत का उल्लंघन है ।

आपकी परिपत्र निर्भरता इस तथ्य से आती है कि आप अपनी Stateवस्तु से बहुत अधिक करने की कोशिश कर रहे हैं ।

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

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


0

क्या किसी नियम पुस्तिका ऑब्जेक्ट के लिए किसी विशेष गेम स्थिति के लिए बाध्य होने की आवश्यकता है, या यह एक नियम पुस्तिका ऑब्जेक्ट के लिए अधिक समझ में आता है, जो एक गेम स्टेट को देखते हुए, रिपोर्ट करेगा कि उस राज्य से कौन सी चालें उपलब्ध हैं (और, सूचना दी है कि, राज्य के बारे में कुछ भी याद नहीं है)? जब तक कि उपलब्ध वस्तु के बारे में कुछ नहीं कहा जाता है, जो उपलब्ध चालों के बारे में पूछा जाता है, खेल राज्य की स्मृति को बनाए रखता है, तो इसके लिए कोई संदर्भ बनाए रखने की आवश्यकता नहीं है।

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

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


-2

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


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