जैसा कि आप प्रोग्रामिंग सीखते हैं, आप कोड लिखने में काफी समय व्यतीत करते हैं। अधिकांश नौसिखिए डेवलपर्स का मानना ​​है कि वे भविष्य में यही करेंगे। यह आंशिक रूप से सच है, लेकिन एक प्रोग्रामर के काम में कोड को बनाए रखना और रीफैक्टरिंग करना भी शामिल है। आज हम रिफैक्टरिंग के बारे में बात करने जा रहे हैं। जावा में रिफैक्टरिंग कैसे काम करती है - 1

CodeGym पर रिफैक्टरिंग

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

रिफैक्टरिंग क्या है?

यह अपनी कार्यक्षमता को बदले बिना कोड की संरचना को बदल रहा है। उदाहरण के लिए, मान लीजिए कि हमारे पास एक ऐसी विधि है जो 2 संख्याओं की तुलना करती है और यदि पहला बड़ा है तो सही और गलत है अन्यथा:

    public boolean max(int a, int b) {
        if(a > b) {
            return true;
        } else if (a == b) {
            return false;
        } else {
            return false;
        }
    }
यह काफी बोझिल कोड है। शुरुआती भी शायद ही कभी ऐसा कुछ लिखेंगे, लेकिन एक मौका है। if-elseयदि आप 6-पंक्ति विधि को अधिक संक्षिप्त रूप से लिख सकते हैं तो ब्लॉक का उपयोग क्यों करें ?

 public boolean max(int a, int b) {
      return a > b;
 }
अब हमारे पास एक सरल और सुरुचिपूर्ण विधि है जो उपरोक्त उदाहरण के समान ही ऑपरेशन करती है। रिफैक्टरिंग इस तरह काम करती है: आप कोड के सार को प्रभावित किए बिना उसकी संरचना को बदलते हैं। ऐसी कई रीफ़ैक्टरिंग विधियाँ और तकनीकें हैं जिन पर हम करीब से नज़र डालेंगे।

आपको रिफैक्टरिंग की आवश्यकता क्यों है?

कई कारण हैं। उदाहरण के लिए, कोड में सरलता और संक्षिप्तता प्राप्त करने के लिए। इस सिद्धांत के समर्थकों का मानना ​​है कि कोड जितना संभव हो उतना संक्षिप्त होना चाहिए, भले ही इसे समझने के लिए कई दर्जन टिप्पणियों की आवश्यकता हो। अन्य डेवलपर्स आश्वस्त हैं कि टिप्पणियों की न्यूनतम संख्या के साथ इसे समझने योग्य बनाने के लिए कोड को फिर से सक्रिय किया जाना चाहिए। प्रत्येक टीम अपनी स्थिति अपनाती है, लेकिन याद रखें कि रिफैक्टरिंग का मतलब कमी नहीं हैइसका मुख्य उद्देश्य कोड की संरचना में सुधार करना है। इस समग्र उद्देश्य में कई कार्य शामिल किए जा सकते हैं:
  1. रिफैक्टरिंग अन्य डेवलपर्स द्वारा लिखे गए कोड की समझ में सुधार करता है।
  2. यह बग्स को खोजने और ठीक करने में मदद करता है।
  3. यह सॉफ्टवेयर विकास की गति को तेज कर सकता है।
  4. कुल मिलाकर, यह सॉफ्टवेयर डिजाइन में सुधार करता है।
यदि लंबे समय तक रिफैक्टरिंग नहीं की जाती है, तो विकास में कठिनाइयों का सामना करना पड़ सकता है, जिसमें काम पर पूर्ण विराम भी शामिल है।

"कोड गंध"

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

अनुचित रूप से बड़ी कक्षाएं और विधियां

कक्षाएं और विधियाँ बोझिल हो सकती हैं, उनके विशाल आकार के कारण प्रभावी रूप से ठीक से काम करना असंभव है।

बड़ा वर्ग

इस तरह के वर्ग में बड़ी संख्या में कोड की पंक्तियाँ और कई अलग-अलग विधियाँ होती हैं। एक डेवलपर के लिए आमतौर पर एक नया बनाने के बजाय मौजूदा वर्ग में एक सुविधा जोड़ना आसान होता है, यही वजह है कि वर्ग बढ़ता है। एक नियम के रूप में, ऐसी कक्षा में बहुत अधिक कार्यक्षमता भरी हुई है। इस मामले में, यह कार्यक्षमता के हिस्से को एक अलग वर्ग में ले जाने में मदद करता है। हम इसके बारे में रीफैक्टरिंग तकनीकों के अनुभाग में अधिक विस्तार से बात करेंगे।

लंबी विधि

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

लंबी विधि को दोबारा करने के लिए दो नियम हैं:

  1. यदि आप किसी विधि को लिखते समय कोई टिप्पणी जोड़ना चाहते हैं, तो आपको कार्यक्षमता को एक अलग विधि में रखना चाहिए।
  2. यदि कोई विधि कोड की 10-15 से अधिक पंक्तियाँ लेती है, तो आपको उन कार्यों और उप-कार्यों की पहचान करनी चाहिए जो वह करता है और उप-कार्यों को एक अलग विधि में रखने का प्रयास करें।

लंबी विधि को समाप्त करने के कुछ तरीके हैं:

  • विधि की कार्यक्षमता के भाग को एक अलग विधि में ले जाएँ
  • यदि स्थानीय चर आपको कार्यक्षमता के भाग को स्थानांतरित करने से रोकते हैं, तो आप संपूर्ण वस्तु को दूसरी विधि में स्थानांतरित कर सकते हैं।

बहुत सारे आदिम डेटा प्रकारों का उपयोग करना

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

बहुत अधिक पैरामीटर

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

डेटा के समूह

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

समाधान जो ओओपी सिद्धांतों का उल्लंघन करते हैं

ये "गंध" तब होती है जब कोई डेवलपर उचित OOP डिज़ाइन का उल्लंघन करता है। ऐसा तब होता है जब वह ओओपी क्षमताओं को पूरी तरह से नहीं समझता है और उन्हें पूरी तरह से या ठीक से उपयोग करने में विफल रहता है।

वंशानुक्रम का उपयोग करने में विफलता

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

स्विच स्टेटमेंट

एक बयान में क्या गलत हो सकता है switch? जब यह बहुत जटिल हो जाता है तो यह बुरा होता है। बड़ी संख्या में नेस्टेड ifकथन संबंधित समस्या है।

विभिन्न इंटरफेस के साथ वैकल्पिक कक्षाएं

कई वर्ग एक ही काम करते हैं, लेकिन उनके तरीकों के अलग-अलग नाम हैं।

अस्थायी क्षेत्र

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

गंध जो संशोधन को कठिन बनाती है

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

समानांतर वंशानुक्रम पदानुक्रम

यह समस्या स्वयं प्रकट होती है जब एक वर्ग को उपवर्गित करने के लिए आपको एक अलग वर्ग के लिए एक और उपवर्ग बनाने की आवश्यकता होती है।

समान रूप से वितरित निर्भरताएँ

किसी भी संशोधन के लिए आपको कक्षा के सभी उपयोगों (निर्भरता) को देखने और बहुत से छोटे परिवर्तन करने की आवश्यकता होती है। एक परिवर्तन — अनेक वर्गों में संपादन।

संशोधनों का जटिल वृक्ष

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

"कचरे की गंध"

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

एक विधि में बड़ी संख्या में टिप्पणियाँ

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

डुप्लिकेट कोड

विभिन्न वर्ग या विधियाँ कोड के समान ब्लॉक का उपयोग करती हैं।

आलसी वर्ग

एक वर्ग बहुत कम कार्यक्षमता लेता है, हालांकि इसे बड़े होने की योजना बनाई गई थी।

अप्रयुक्त कोड

कोड में एक वर्ग, विधि या चर का उपयोग नहीं किया जाता है और यह मृत भार है।

अत्यधिक कनेक्टिविटी

गंध की इस श्रेणी को कोड में बड़ी संख्या में अनुचित संबंधों की विशेषता है।

बाहरी तरीके

एक विधि अपने स्वयं के डेटा की तुलना में किसी अन्य वस्तु के डेटा का अधिक बार उपयोग करती है।

अनुचित अंतरंगता

एक वर्ग दूसरे वर्ग के कार्यान्वयन विवरण पर निर्भर करता है।

लंबी क्लास कॉल

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

टास्क-डीलर वर्ग

किसी कार्य को दूसरी कक्षा में भेजने के लिए ही एक वर्ग की आवश्यकता होती है। शायद इसे हटा दिया जाना चाहिए?

रिफैक्टरिंग तकनीक

नीचे हम बुनियादी रीफैक्टरिंग तकनीकों पर चर्चा करेंगे जो वर्णित कोड गंध को खत्म करने में मदद कर सकती हैं।

एक वर्ग निकालें

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

class Human {
    private String name;
    private String age;
    private String country;
    private String city;
    private String street;
    private String house;
    private String quarter;
 
    public String getFullAddress() {
        StringBuilder result = new StringBuilder();
        return result
                        .append(country)
                        .append(", ")
                        .append(city)
                        .append(", ")
                        .append(street)
                        .append(", ")
                        .append(house)
                        .append(" ")
                        .append(quarter).toString();
    }
 }
पता जानकारी और संबंधित विधि (डेटा प्रोसेसिंग व्यवहार) को एक अलग वर्ग में रखना अच्छा अभ्यास है:

 class Human {
    private String name;
    private String age;
    private Address address;
 
    private String getFullAddress() {
        return address.getFullAddress();
    }
 }
 class Address {
    private String country;
    private String city;
    private String street;
    private String house;
    private String quarter;
 
    public String getFullAddress() {
        StringBuilder result = new StringBuilder();
        return result
                        .append(country)
                        .append(", ")
                        .append(city)
                        .append(", ")
                        .append(street)
                        .append(", ")
                        .append(house)
                        .append(" ")
                        .append(quarter).toString();
    }
 }

एक विधि निकालें

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

    public void calcQuadraticEq(double a, double b, double c) {
        double D = b * b - 4 * a * c;
        if (D > 0) {
            double x1, x2;
            x1 = (-b - Math.sqrt(D)) / (2 * a);
            x2 = (-b + Math.sqrt(D)) / (2 * a);
            System.out.println("x1 = " + x1 + ", x2 = " + x2);
        }
        else if (D == 0) {
            double x;
            x = -b / (2 * a);
            System.out.println("x = " + x);
        }
        else {
            System.out.println("Equation has no roots");
        }
    }
हम अलग-अलग तरीकों से तीन संभावित विकल्पों में से प्रत्येक की गणना करते हैं:

    public void calcQuadraticEq(double a, double b, double c) {
        double D = b * b - 4 * a * c;
        if (D > 0) {
            dGreaterThanZero(a, b, D);
        }
        else if (D == 0) {
            dEqualsZero(a, b);
        }
        else {
            dLessThanZero();
        }
    }
 
    public void dGreaterThanZero(double a, double b, double D) {
        double x1, x2;
        x1 = (-b - Math.sqrt(D)) / (2 * a);
        x2 = (-b + Math.sqrt(D)) / (2 * a);
        System.out.println("x1 = " + x1 + ", x2 = " + x2);
    }
 
    public void dEqualsZero(double a, double b) {
        double x;
        x = -b / (2 * a);
        System.out.println("x = " + x);
    }
 
    public void dLessThanZero() {
        System.out.println("Equation has no roots");
    }
प्रत्येक विधि का कोड बहुत छोटा और समझने में आसान हो गया है।

एक संपूर्ण वस्तु को पास करना

जब किसी विधि को मापदंडों के साथ कहा जाता है, तो आप कभी-कभी इस तरह का कोड देख सकते हैं:

 public void employeeMethod(Employee employee) {
     // Some actions
     double yearlySalary = employee.getYearlySalary();
     double awards = employee.getAwards();
     double monthlySalary = getMonthlySalary(yearlySalary, awards);
     // Continue processing
 }
 
 public double getMonthlySalary(double yearlySalary, double awards) {
      return (yearlySalary + awards)/12;
 }
employeeMethodमूल्यों को प्राप्त करने और उन्हें आदिम चर में संग्रहीत करने के लिए समर्पित 2 पूरी पंक्तियाँ हैं । कभी-कभी ऐसे निर्माणों में 10 पंक्तियां लग सकती हैं। ऑब्जेक्ट को पास करना और आवश्यक डेटा निकालने के लिए इसका उपयोग करना बहुत आसान है:

 public void employeeMethod(Employee employee) {
     // Some actions
     double monthlySalary = getMonthlySalary(employee);
     // Continue processing
 }
 
 public double getMonthlySalary(Employee employee) {
     return (employee.getYearlySalary() + employee.getAwards())/12;
 }

सरल, संक्षिप्त और संक्षिप्त।

तार्किक रूप से क्षेत्रों को समूहीकृत करना और उन्हें एक अलग classDespiteतथ्य में स्थानांतरित करना कि ऊपर दिए गए उदाहरण बहुत सरल हैं, और जब आप उन्हें देखते हैं, तो आप में से कई पूछ सकते हैं, "यह कौन करता है?", कई डेवलपर्स लापरवाही के कारण ऐसी संरचनात्मक त्रुटियां करते हैं, कोड को रिफैक्टर करने की अनिच्छा, या बस "यह काफी अच्छा है" का एक दृष्टिकोण।

रिफैक्टरिंग क्यों प्रभावी है

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

रिफैक्टरिंग में खुद को और डुबोने के लिए संसाधन

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