मेरे जटिल राज्य वर्गों और उनके परीक्षण को सरल कैसे करें?


9

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

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

एक उदाहरण के रूप में और चीजों को आसान और स्पष्ट रखने के लिए, मान लें कि रोबोट और कार मेरी परियोजना में कक्षाएं थीं।

तो, रोबोट क्लास में मेरे पास निम्न पैटर्न में बहुत सारे तरीके होंगे:

  • नींद (); isSleepAvaliable ();
  • जाग(); isAwakeAvaliable ();
  • (दिशा) चलना; isWalkAvaliable ();
  • शूट (दिशा); isShootAvaliable ();
  • turnOnAlert (); isTurnOnAlertAvailable ();
  • turnOffAlert (); isTurnOffAlertAvailable ();
  • पुनर्भरण (); isRechargeAvailable ();
  • बिजली बंद(); isPowerOffAvailable ();
  • stepInCar (कार); isStepInCarAvailable ();
  • stepOutCar (कार); isStepOutCarAvailable ();
  • आत्म विनाश(); isSelfDestructAvailable ();
  • मरने (); isDieAvailable ();
  • जिंदा है(); जगा हुआ(); isAlertOn (); getBatteryLevel (); getCurrentRidingCar (); getAmmo ();
  • ...

कार वर्ग में, यह समान होगा:

  • चालू करो(); isTurnOnAvaliable ();
  • बंद करें(); isTurnOffAvaliable ();
  • (दिशा) चलना; isWalkAvaliable ();
  • फिर से ईधन (); isRefuelAvailable ();
  • आत्म विनाश(); isSelfDestructAvailable ();
  • दुर्घटना(); isCrashAvailable ();
  • isOperational (); ison (); getFuelLevel (); getCurrentPassenger ();
  • ...

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

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

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

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

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

EDIT:
मुझे लगता है कि मैं राज्य मशीनों के बारे में स्पष्ट नहीं था। रोबोट स्वयं एक राज्य मशीन है, जिसमें "स्लीपिंग", "जाग", "रिचार्जिंग", "डेड" आदि राज्य शामिल हैं। कार एक अन्य राज्य मशीन है।

EDIT 2: इस मामले में कि आप उत्सुक हैं कि मेरा सिस्टम वास्तव में क्या है, बातचीत करने वाली कक्षाएं सर्वर, IPAddress, डिस्क, बैकअप, उपयोगकर्ता, SoftwareLicense, आदि जैसी चीजें हैं। रोबोट और कार का परिदृश्य सिर्फ एक ऐसा मामला है जो मुझे मिला है। मेरी समस्या को समझाने के लिए यह काफी सरल होगा।


क्या आपने कोड समीक्षा पर विचार करने पर विचार किया । इसके अलावा, तुम्हारी तरह डिजाइन के लिए मैं
एक्सट्रा

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

@gnat क्या आप इसका उदाहरण दे सकते हैं कि मैं दिए गए रोबोट और कार परिदृश्य में एक्सट्रैक्ट क्लास कैसे लागू करूंगा?
विक्टर स्टैफुसा

मैं रोबोट से कार से संबंधित सामान एक अलग कक्षा में निकालूंगा। मैं नींद से संबंधित सभी तरीकों को भी निकालता हूँ + एक समर्पित वर्ग में जागता हूँ। अन्य "उम्मीदवार" जो निष्कर्षण के लायक प्रतीत होते हैं वे पावर + रिचार्ज के तरीके, आंदोलन से संबंधित सामान हैं। आदि पर ध्यान दें क्योंकि यह रीफैक्टरिंग है, रोबोट के लिए बाहरी एपीआई शायद रहना चाहिए; पहले चरण में मैंने केवल इंटर्नल्स को संशोधित किया। BTDTGTTS
gnat

यह एक कोड रिव्यू प्रश्न नहीं है - आर्किटेक्चर वहां ऑफ-टॉपिक है।
माइकल के

जवाबों:


8

राज्य करता है, तो आप पहले से ही उपयोग में न हो डिजाइन पैटर्न, काम का हो सकता।

तो अपने उदाहरण, जारी रखने के लिए - मूल विचार है कि आप प्रत्येक अलग राज्य के लिए एक आंतरिक वर्ग पैदा करते हैं SleepingRobot, AwakeRobot, RechargingRobotऔर DeadRobotवर्गों सब हो सकता है, एक आम इंटरफेस को लागू करने।

Robotकक्षा के तरीकों (जैसे sleep()और isSleepAvaliable()) में सरल कार्यान्वयन होते हैं जो वर्तमान आंतरिक वर्ग को सौंपते हैं।

राज्य के बदलावों को वर्तमान आंतरिक वर्ग को एक अलग से बदलकर लागू किया जाता है।

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


मैं जावा का उपयोग कर रहा हूं।
विक्टर स्टैफुसा

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

3

मुझे आपका कोड नहीं पता है, लेकिन "नींद" पद्धति का उदाहरण लेते हुए, मैं यह मानूंगा कि यह निम्नलिखित "सरल" कोड की तरह कुछ है:

public void sleep() {
 if(!dead && awake) {
  sleeping = true;
  awake = false;
  this.updateState(SLEEPING);
 }
 throw new IllegalArgumentException("robot is either dead or not awake");
}

मुझे लगता है कि आपको एकीकरण परीक्षणों और इकाई परीक्षणों के बीच अंतर करना होगा । एक परीक्षण लिखना जो आपके पूरे मशीन राज्य को गर्त में चलाता है निश्चित रूप से एक बड़ा काम है। छोटी इकाई परीक्षण लिखना जो यह परीक्षण करता है कि आपकी नींद की विधि ठीक से काम करती है या नहीं। इस बिंदु पर आपको यह जानने की ज़रूरत नहीं है कि क्या मशीन राज्य को ठीक से अपडेट किया गया है या यदि "कार" ने इस तथ्य पर सही ढंग से प्रतिक्रिया दी है कि मशीन राज्य को "रोबोट" द्वारा अपडेट किया गया है ... आदि।

ऊपर दिए गए कोड को देखते हुए, मैं "machineState" ऑब्जेक्ट का मजाक उड़ाऊंगा और मेरा पहला टेस्ट होगा:

testSleep_dead() {
 robot.dead = true;
 robot.awake = false;
 robot.setState(AWAKE);
 try {
  robot.sleep();
  fail("should have got an exception");
 } catch(Exception e) {
  assertTrue(e instanceof IllegalArgumentException);
  assertEquals("robot is either dead or not awake", e.getMessage());
 }
}

मेरी व्यक्तिगत राय है कि इस तरह की छोटी इकाई परीक्षणों को लिखना पहली बात होनी चाहिए। आपने लिखा है कि:

Testcases setups बहुत जटिल हैं, क्योंकि उन्हें व्यायाम करने के लिए एक महत्वपूर्ण जटिल दुनिया बनाने की आवश्यकता है।

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

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

अंत में, यह किया जा सकता है कि क्या आपका कोड जटिल है (जैसा कि आपने अभी कहा है), या आपके द्वारा रिफैक्ट किए जाने के बाद।


मुझे लगता है कि मैं राज्य मशीनों के बारे में स्पष्ट नहीं था। रोबोट स्वयं एक राज्य मशीन है, जिसमें "स्लीपिंग", "जाग", "रिचार्जिंग", "डेड" आदि राज्य शामिल हैं। कार एक अन्य राज्य मशीन है।
विक्टर स्टैफुसा

@ विक्टर ठीक है, यदि आप चाहते हैं तो मेरे उदाहरण कोड को ठीक करने के लिए स्वतंत्र महसूस करें। जब तक आप मुझे अन्यथा नहीं बताते, मुझे लगता है कि यूनिट परीक्षणों पर मेरी बात अभी भी मान्य है, मुझे उम्मीद है कि कम से कम।
जलयाने

मैंने उदाहरण को सही किया। मेरे पास इसे आसानी से दिखाई देने के लिए निजीकरण नहीं है, इसलिए इसे पहले सहकर्मी की समीक्षा करनी चाहिए। आपकी टिप्पणी सहायक है।
विक्टर स्टैफुसा

2

मैं इंटरफ़ेस पृथक्करण सिद्धांत पर विकिपीडिया लेख के "मूल" खंड को पढ़ रहा था , और मुझे यह प्रश्न याद दिलाया गया था।

मैं लेख उद्धृत करूंगा। समस्या: "... एक मुख्य नौकरी वर्ग .... विभिन्न ग्राहकों के विभिन्न प्रकारों के लिए विशिष्ट तरीकों के साथ एक मोटा वर्ग।" समाधान: "... नौकरी वर्ग और उसके सभी ग्राहकों के बीच इंटरफेस की एक परत ..."

आपकी समस्या को लगता है कि एक जेरॉक्स का एक क्रमचय था। एक वसा वर्ग के बजाय आपके पास दो हैं, और ये दो वसा वर्ग बहुत सारे ग्राहकों के बजाय एक दूसरे से बात कर रहे हैं।

क्या आप तरीकों को बातचीत के प्रकार से जोड़ सकते हैं और फिर प्रत्येक प्रकार के लिए एक इंटरफ़ेस वर्ग बना सकते हैं? उदाहरण के लिए: RobotPowerInterface, RobotNavigationInterface, RobotAlarmInterface वर्ग?

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