SOLID सिद्धांत क्यों महत्वपूर्ण हैं और डेवलपर्स उन्हें क्यों छोड़ देते हैं

SOLID सिद्धांत क्यों महत्वपूर्ण हैं और डेवलपर्स उन्हें क्यों छोड़ देते हैं

हर कोई SOLID सिद्धांतों के बारे में बात करता है। आपका सीनियर डेवलपर कोड रिव्यू में उनका जिक्र करता है। आपके आर्किटेक्चर दस्तावेज़ उनका संदर्भ देते हैं। लेकिन असली परियोजनाएं अनरक्षणीय कूड़े क्यों बन जाती हैं भले ही टीमें SOLID को “जानती” हों? उत्तर: SOLID को समझना और वास्तव में इसके साथ निर्माण करना दो बिल्कुल अलग चीजें हैं [1]।

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

SOLID क्या है, और आपको इसकी परवाह क्यों करनी चाहिए?

SOLID पाँच डिज़ाइन सिद्धांत हैं जो कसे युग्मन को कम करते हैं और कोड रखरखाव में सुधार करते हैं [1]। संक्षिप्त नाम निम्नलिखित के लिए खड़ा है:

  • S (एकल जिम्मेदारी सिद्धांत) — एक वर्ग के परिवर्तन का केवल एक कारण होना चाहिए
  • O (खुला/बंद सिद्धांत) — वर्ग विस्तार के लिए खुले होने चाहिए, संशोधन के लिए बंद
  • L (Liskov प्रतिस्थापन सिद्धांत) — उप-प्रकार अपने आधार प्रकारों के लिए प्रतिस्थापित होने योग्य होने चाहिए
  • I (इंटरफेस विभाजन सिद्धांत) — क्लाइंट्स को उन इंटरफेस पर निर्भर नहीं होना चाहिए जिनका वे उपयोग नहीं करते
  • D (निर्भरता व्युत्क्रमण सिद्धांत) — ठोस कार्यान्वयन पर नहीं, अमूर्तताओं पर निर्भर करें

इन सिद्धांतों के बिना, कोडबेस कठोर हो जाता है। एक स्थान पर एक परिवर्तन पाँच अन्य स्थानों को तोड़ देता है। नई सुविधाओं के लिए विरासत कोड को छूना आवश्यक होता है जो “काम करता है लेकिन कोई इसे छूना नहीं चाहता”। परीक्षण लिखना असंभव हो जाता है। अधिकांश परियोजनाएं यहीं रहती हैं [2]।

SOLID के साथ, आपका कोडबेस साँस लेता है। परिवर्तन अलग-थलग रहते हैं। नई सुविधाएं प्रणाली में नहीं फैलती। परीक्षण अलगाव में चलते हैं। नए डेवलपर्स को ऑनबोर्ड करने के लिए 10 साल पुराने कोड की पुरातात्विक खोज की आवश्यकता नहीं होती।

वास्तविक-दुनिया उल्लंघन: SOLID कहां गायब है

भगवान वर्ग — सब कुछ एक जगह में

मैंने यह सैकड़ों बार देखा है। एक UserManager वर्ग जो उपयोगकर्ता डेटा, ईमेल भेजना, पासवर्ड हैशिंग, लॉगिंग, भुगतान प्रसंस्करण, और प्रमाणीकरण को संभालता है। सब कुछ एक फाइल में। सब कुछ उलझा हुआ [3]।

class UserManager {
    public void createUser(String email, String password) {
        validateEmail(email);
        hashPassword(password);
        saveToDatabase(email, password);
        sendWelcomeEmail(email);
        logUserCreation(email);
        chargeSignupFee(email);
        trackAnalyticsEvent(email);
    }
    
    private void sendWelcomeEmail(String email) { /* ... */ }
    private void saveToDatabase(String email, String password) { /* ... */ }
    private void chargeSignupFee(String email) { /* ... */ }
    // 50 more methods doing unrelated things
}

यह एकल जिम्मेदारी सिद्धांत का पूर्ण उल्लंघन है। ईमेल भेजने का तरीका बदलना चाहते हैं? आप UserManager को संशोधित करते हैं। भुगतान लॉजिक बदलना चाहते हैं? आप UserManager को संशोधित करते हैं। नया लॉग प्रारूप जोड़ना चाहते हैं? आप UserManager को संशोधित करते हैं। अब आप भुगतान कोड का परीक्षण कर रहे हैं जब आप केवल स्वागत ईमेल टेम्पलेट को ट्विक करना चाहते थे।

फिक्स: अलग-अलग वर्गों में विभाजित करें [1]। एक उपयोगकर्ता दृढ़ता को संभालता है। दूसरा ईमेल भेजता है। तीसरा भुगतान प्रसंस्करण करता है। चौथा लॉगिंग को संभालता है। अब प्रत्येक वर्ग के परिवर्तन का केवल एक कारण है।

कसा युग्मन — भुगतान प्रणाली समस्या

आप कोड लिखते हैं जो सीधे एक विशिष्ट भुगतान प्रदाता पर निर्भर करता है: StripePaymentProcessor। यह काम करता है। आपका व्यवसाय बढ़ता है। अब आप PayPal का समर्थन करना चाहते हैं। या क्रिप्टोकरेंसी। या कुछ नई स्थानीय भुगतान प्रणाली [4]।

class OrderService {
    private StripePaymentProcessor stripe = new StripePaymentProcessor();
    
    public void processOrder(Order order) {
        // ... validation code ...
        stripe.charge(order.getAmount());
    }
}

PayPal समर्थन जोड़ने के लिए, आप OrderService को संशोधित करते हैं। क्रिप्टो जोड़ने के लिए, आप इसे फिर से संशोधित करते हैं। आप हर भुगतान प्रदाता के लिए व्यावसायिक तर्क कोड को संशोधित कर रहे हैं। यह खुला/बंद सिद्धांत का उल्लंघन करता है — आपका कोड विस्तार के लिए खुला होना चाहिए (नई भुगतान प्रकार) लेकिन संशोधन के लिए बंद [2]।

फिक्स: एक अमूर्तता पर निर्भर करें:

interface PaymentGateway {
    void charge(BigDecimal amount);
}

class OrderService {
    private PaymentGateway gateway;
    
    public OrderService(PaymentGateway gateway) {
        this.gateway = gateway;
    }
    
    public void processOrder(Order order) {
        gateway.charge(order.getAmount());
    }
}

PayPal जोड़ना? PayPalPaymentGateway बनाएं, इसे इंजेक्ट करें, हो गया। आपका OrderService कभी नहीं बदलता [4]।

फैट इंटरफेस — अप्रयुक्त विधियों के कार्यान्वयन को मजबूर करना

आप एक इंटरफेस बनाते हैं जो “व्यापक” है:

interface PaymentProcessor {
    void charge(BigDecimal amount);
    void refund(BigDecimal amount);
    void setupRecurringPayment(BigDecimal amount, Frequency frequency);
    void validateCard(String cardNumber);
    void handleChargebackDispute(String transactionId);
}

फिर एक CryptoCurrencyPayment वर्ग इस इंटरफेस को लागू करता है। सिवाय क्रिप्टो आवर्ती भुगतान नहीं करता। यह चार्जबैक नहीं करता। डेवलपर्स आधी विधियों के लिए NotImplementedException फेंकने के लिए मजबूर हैं [3]।

यह इंटरफेस विभाजन सिद्धांत का उल्लंघन करता है। इसके बजाय छोटे, केंद्रित इंटरफेस बनाएं:

interface Chargeable {
    void charge(BigDecimal amount);
}

interface Refundable {
    void refund(BigDecimal amount);
}

interface RecurringBillable {
    void setupRecurring(BigDecimal amount);
}

अब CryptoCurrencyPayment केवल Chargeable को लागू करता है। कोई नकली कार्यान्वयन नहीं। कोई भ्रम नहीं [3]।

SOLID सिद्धांतों का पालन करना क्यों इतना मुश्किल है

नियमों को याद रखने की तुलना में यह समझना अधिक महत्वपूर्ण है कि हम SOLID का उल्लंघन क्यों करते हैं।

समय सीमा जाल

आप स्प्रिंट के 9वें दिन हैं 10-दिनों की स्प्रिंट में से। सुविधा पूरी होनी चाहिए। आप UserManager वर्ग को रीफैक्टर कर सकते हैं, इसे सही ढंग से विभाजित कर सकते हैं, निर्भरताओं को इंजेक्ट कर सकते हैं। या आप 5 मिनट में मौजूदा वर्ग में एक विधि जोड़ सकते हैं।

आप विधि जोड़ते हैं [5]।

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

सिद्धांतों की अस्पष्टता

एकल जिम्मेदारी सरल लगता है: परिवर्तन का एक कारण। लेकिन “एक कारण” क्या है? क्या ईमेल और पासवर्ड सत्यापन के साथ एक User वर्ग एक या दो जिम्मेदारी है? क्या चार्ज और रिफंड दोनों को संभालने वाली Payment वर्ग एक जिम्मेदारी है? [5]

विभिन्न डेवलपर्स इसे अलग तरह से व्याख्यायित करते हैं। मैंने टीमों को देखा है जो उपयोगकर्ता ईमेल को पाँच अलग-अलग वर्गों में विभाजित करते हैं (अत्यधिक इंजीनियरिंग)। मैंने अन्य टीमों को एक वर्ग में 50 विधियाँ डालते देखा है (कम इंजीनियरिंग)। कोई चमकदार रेखा नहीं है।

अत्यधिक इंजीनियरिंग और अत्यधिक अमूर्तता

जूनियर डेवलपर्स SOLID के बारे में पढ़ते हैं और पागल हो जाते हैं। वे हर समस्या पर हर सिद्धांत को लागू करते हैं। एक सरल फॉर्म सत्यापन वर्ग को तीन अमूर्त वर्गों, दो इंटरफेस, और एक निर्भरता इंजेक्शन कंटेनर में रीफैक्टर किया जाता है [5]।

कोड पढ़ने में कठिन हो जाता है, आसान नहीं। आप जो तीन पंक्तियों का तर्क होना चाहिए उसे समझने के लिए पाँच फाइलों के बीच कूदने की आवश्यकता है। आपका टेस्ट सूट वास्तविक कोड से अधिक जटिल हो जाता है।

SOLID सिद्धांत उपकरण हैं, कानून नहीं। एक सरल स्क्रिप्ट को SOLID की जरूरत नहीं है। एक उपयोगिता फंक्शन को SOLID की जरूरत नहीं है। एक बार का डेटा प्रसंस्करण कार्य को SOLID की जरूरत नहीं है। SOLID उन कोडबेस के लिए महत्वपूर्ण है जो वर्षों तक रहेंगे और निरंतर परिवर्तन की आवश्यकता होगी [5]।

टीम संरेखण मुश्किल है

भले ही आपकी टीम SOLID का पालन करने के लिए सहमत हो, इसकी व्याख्या निरंतर बहस का स्रोत है [5]।

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

छह महीने में, आपका कोडबेस तीन अलग-अलग लोगों की तरह दिखता है जिन्होंने SOLID की तीन अलग-अलग व्याख्याओं के साथ इसे लिखा है। कोड रिव्यू सुविधाओं को शिप करने के बजाय सिद्धांतों पर बहस करने के बारे में हो जाता है।

विरासत कोड रीफैक्टर करना महंगा है

अधिकांश परियोजनाएं स्वच्छ रूप से शुरू नहीं होती। वे एक फाइल के साथ सब कुछ करना शुरू करते हैं। फिर यह बढ़ता है। फिर SOLID महत्वपूर्ण हो जाता है। फिर इसे रीफैक्टर करना एक दुःस्वप्न हो जाता है।

आप केवल UserManager वर्ग को विभाजित नहीं कर सकते। इसके पास पूरे कोडबेस में 500 निर्भरताएं हैं। डेटाबेस कनेक्शन स्ट्रिंग्स कोडित। कॉन्फ़िगरेशन बेक किया हुआ। वैश्विक स्थिति हर जगह बिखरी हुई [5]।

इसे “सही ढंग से” रीफैक्टर करना 200 फाइलों को छूना मतलब है। कुछ तोड़ने का जोखिम अधिक है। आपके परीक्षण (यदि वे मौजूद हैं) धीमे और नाजुक हैं क्योंकि वे God वर्ग पर निर्भर हैं।

क्यों परियोजनाओं को अभी भी SOLID की जरूरत है

इन सभी चुनौतियों के बावजूद, जो परियोजनाएं SOLID का पालन करती हैं वे नाटकीय रूप से बनाए रखना आसान होती हैं [1]। यह बात है: आप SOLID का उल्लंघन करने की पीड़ा पहले कुछ महीनों के लिए महसूस नहीं करते। आप एक साल बाद महसूस करते हैं।

एक साल बाद God वर्ग में सुविधाएं जोड़ने के बाद, वह एक भुगतान प्रसंस्करण विधि अब 200 पंक्तियों लंबी है। यह तीन अलग-अलग भुगतान प्रदाताओं, विरासत भुगतान प्रकारों, और एक VIP उपयोगकर्ता स्तर प्रणाली के लिए किनारे के मामलों को संभालता है। जब आपको चौथा प्रदाता जोड़ने की आवश्यकता होती है, तो आप इस विधि को बदलने से डरते हैं।

एक साल बाद कसे युग्मन के बाद, डेटाबेस परत को रीफैक्टर करने का मतलब 50 फाइलों को छूना है। एक साल बाद फैट इंटरफेस के बाद, नया कार्यान्वयन जोड़ने के लिए पाँच वर्गों में स्टब विधियाँ लिखनी पड़ती हैं।

SOLID आगे महंगा है। यह लंबी अवधि में सस्ता है।

जो परियोजनाएं इस सबक को कभी नहीं सीखीं वे “विरासत कोड” के साथ समाप्त होती हैं जिसे परिवर्तित नहीं किया जा सकता क्योंकि यह बहुत जोखिम भरा है। नई सुविधाएं हफ्तों के बजाय दिन लगते हैं। सरल बग फिक्स को छह उलझे हुए वर्गों को समझने की आवश्यकता होती है। तब लोग पूरी प्रणाली को फिर से लिखते हैं (जो लगभग कभी समाधान नहीं होता)।

End

स्रोत

  1. SOLID सिद्धांत वास्तविक जीवन उदाहरणों के साथ - GeeksforGeeks
  2. SOLID डिज़ाइन सिद्धांत समझाए गए: बेहतर सॉफ्टवेयर आर्किटेक्चर बनाना - DigitalOcean
  3. SOLID डिज़ाइन सिद्धांत: एकल जिम्मेदारी समझाया - Stackify
  4. Java में निर्भरता व्युत्क्रमण सिद्धांत - Baeldung
  5. SOLID डिज़ाइन सिद्धांतों का उपयोग करते समय 5 समस्याएं - Better Programming