SOLID डिज़ाइन सिद्धांत सबसे महत्वपूर्ण डिज़ाइन सिद्धांत हैं जिन्हें आपको क्लीन कोड लिखने के लिए जानने की आवश्यकता है। SOLID सिद्धांतों पर ठोस पकड़ होना किसी भी प्रोग्रामर के लिए एक अनिवार्य कौशल है। वे वह आधार हैं जिस पर अन्य डिज़ाइन पैटर्न विकसित किए जाते हैं। इस लेख में, हम कुछ वास्तविक जीवन के उदाहरणों का उपयोग करके SOLID डिज़ाइन सिद्धांतों से निपटेंगे और उनके महत्व को समझेंगे। बहुरूपता, अमूर्तता और वंशानुक्रम के साथ, उद्देश्य-उन्मुख प्रोग्रामिंग में अच्छा होने के लिए SOLID सिद्धांत वास्तव में महत्वपूर्ण हैं। ठोस सिद्धांत महत्वपूर्ण क्यों हैं? SOLID सिद्धांत कई कारणों से महत्वपूर्ण हैं: SOLID सिद्धांत हमें स्वच्छ और रखरखाव योग्य कोड लिखने की अनुमति देते हैं: जब हम एक नया प्रोजेक्ट शुरू करते हैं, तो शुरू में कोड की गुणवत्ता अच्छी होती है क्योंकि हमारे पास सीमित सुविधाओं का सेट होता है जिन्हें हम लागू करते हैं। हालाँकि, जैसे-जैसे हम अधिक सुविधाएँ शामिल करते हैं, कोड अव्यवस्थित होने लगता है। ठोस सिद्धांत अमूर्तता, बहुरूपता और वंशानुक्रम की नींव पर निर्मित होते हैं और सामान्य उपयोग के मामलों के लिए डिजाइन पैटर्न का नेतृत्व करते हैं। इन डिज़ाइन पैटर्न को समझने से प्रोग्रामिंग में सामान्य उपयोग के मामलों को लागू करने में मदद मिलती है। SOLID सिद्धांत हमें स्वच्छ कोड लिखने में मदद करते हैं जिससे कोड की परीक्षण क्षमता में सुधार होता है। ऐसा इसलिए है क्योंकि कोड मॉड्यूलर और शिथिल युग्मित है। प्रत्येक मॉड्यूल को स्वतंत्र रूप से विकसित और स्वतंत्र रूप से परीक्षण किया जा सकता है। आइए अब वास्तविक दुनिया के उदाहरणों के साथ प्रत्येक SOLID सिद्धांतों को विस्तार से देखें। 1. एकल उत्तरदायित्व सिद्धांत SOLID सिद्धांतों में S का मतलब एकल उत्तरदायित्व सिद्धांत है। एकल उत्तरदायित्व सिद्धांत कहता है कि किसी वर्ग के पास परिवर्तन का केवल एक ही कारण होना चाहिए। यह उन स्थानों की संख्या को सीमित करता है जहां हमें अपने प्रोजेक्ट में अतिरिक्त आवश्यकताओं को शामिल करते समय परिवर्तन करने की आवश्यकता होती है। प्रत्येक वर्ग के पास परिवर्तन का बिल्कुल एक कारण होना चाहिए। उदाहरण के लिए, मान लें कि हम जावा में एक बैंकिंग एप्लिकेशन डिज़ाइन कर रहे हैं जहां हमारे पास एक क्लास है जो डेबिट, क्रेडिट और जैसे बुनियादी संचालन की अनुमति देता है। विधि (जैसे ईमेल, एसएमएस, आदि) नामक एक एनम लेती है और उचित माध्यम से अपडेट भेजती है। हम उसके लिए कोड लिखेंगे जैसा कि नीचे दिखाया गया है। SavingsAccount sendUpdates sendUpdate NotificationMedium public class SavingsAccount { public int balance; public String name; public SavingsAccount(int initialBalance, String name) { this.balance = initialBalance; this.name = name; System.out.println("Created a savings account with balance = " + initialBalance); } public void debit(int amountToDebit) { // debit business logic } public void credit(int amountToCredit) { // credit business logic } public void sendNotification(NotificationMedium medium) { if (medium == NotificationMedium.SMS) { // Send SMS here } else if (medium == NotificationMedium.EMAIL) { // Send Email here } } } public enum NotificationMedium { SMS, EMAIL } अब, यदि आप उपरोक्त वर्ग को देखें, तो यह कई कारणों से बदल सकता है: SavingsAccount यदि वर्ग के मूल तर्क (जैसे , , आदि) में कोई बदलाव है। SavingsAccount debit credit यदि बैंक एक नया अधिसूचना माध्यम (मान लें कि व्हाट्सएप) शुरू करने का निर्णय लेता है। यह SOLID सिद्धांतों में एकल उत्तरदायित्व सिद्धांत का उल्लंघन है। इसे ठीक करने के लिए हम एक अलग क्लास बनाएंगे जो नोटिफिकेशन भेजती है। आइए उपरोक्त कोड को SOLID सिद्धांतों के अनुसार पुनः क्रियान्वित करें public class SavingsAccount { public int balance; public String name; public SavingsAccount(int initialBalance, String name) { this.balance = initialBalance; this.name = name; System.out.println("Created a savings account with balance = " + initialBalance); } public void debit(int amountToDebit) { // debit business logic } public void credit(int amountToCredit) { // credit business logic } public void printBalance() { System.out.println("Name: " + name+ " Account Balance: " + balance); } public void sendNotification(Medium medium) { Sender.sendNotification(medium, this); } } public enum NotificationMedium { SMS, EMAIL } public class Sender { public static void sendNotification(NotificationMedium medium, SavingsAccount account) { // extract account data from the account object if (medium == NotificationMedium.SMS) { //logic to send SMS here } else if (medium == NotificationMedium.EMAIL) { // logic to send Email here } } } अब, चूंकि हमने कोड को दोबारा तैयार कर लिया है, अगर या प्रारूप में कोई बदलाव होता है, तो हम वर्ग को बदल देंगे। हालाँकि, यदि के मूल तर्क में कोई परिवर्तन होता है, तो वर्ग में भी परिवर्तन होंगे। NotificationMedium Sender SavingsAccount SavingsAccount यह उस उल्लंघन को ठीक करता है जो हमने पहले उदाहरण में देखा था। 2. खोलें/बंद करने का सिद्धांत ओपन क्लोज़ सिद्धांत कहता है कि हमें कक्षाओं को इस तरह से डिज़ाइन करना चाहिए ताकि वे विस्तार के लिए खुले हों (अतिरिक्त सुविधाएँ जोड़ने के मामले में) लेकिन संशोधन के लिए बंद हों। संशोधन के लिए बंद होने से हमें दो तरह से मदद मिलती है: कई बार, मूल वर्ग स्रोत उपलब्ध भी नहीं हो पाता है। यह आपके प्रोजेक्ट द्वारा उपभोग की गई निर्भरता हो सकती है। मूल वर्ग को अपरिवर्तित रखने से बग की संभावना कम हो जाती है। चूँकि अन्य वर्ग भी हो सकते हैं जो उस वर्ग पर निर्भर हों जिसे हम संशोधित करना चाहते हैं। ओपन क्लोज़ सिद्धांत का एक उदाहरण देखने के लिए, आइए एक शॉपिंग कार्ट के उदाहरण पर नज़र डालें (जैसे कि ई-कॉमर्स वेबसाइटों पर लागू किया गया)। मैं नाम से एक क्लास बनाने जा रहा हूं जिसमें उन की एक सूची होगी जिन्हें आप इसमें जोड़ सकते हैं। वस्तु के प्रकार और उस पर कराधान के आधार पर हम एक ऐसी विधि बनाना चाहते हैं जो वर्ग के अंदर कुल कार्ट मूल्य की गणना करे। Cart Item Cart कक्षाएं विस्तार के लिए खुली रहनी चाहिए और संशोधन के लिए बंद होनी चाहिए import java.util.ArrayList; import java.util.List; public class Cart { private List<Item> items; public Cart() { this.items = new ArrayList<>(); } public void addToCart(Item item) { items.add(item); } public double calculateCartValue() { double value = 0.0; for(Item item: items) { if (item.getItemType() == GIFT) { // 8% tax on gift + 2% gift wrap cost value += (item.getValue()*1.08) + item.getValue()*0.02 } else if (item.getItemType() == ItemType.ELECTRONIC_ITEM) { value += (item.getValue()*1.11); } else { value += item.getValue()*1.10; } } return value; } } @Getter @Setter public abstract class Item { protected double price; private ItemType itemType; public double getValue() { return price; } } public enum ItemType { ELECTRONIC, GIFT } उपरोक्त उदाहरण में, विधि कार्ट के अंदर सभी आइटमों पर पुनरावृत्ति करके और आइटम के प्रकार के आधार पर तर्क का आह्वान करके कार्ट मूल्य की गणना करती है। calculateCartValue हालाँकि यह कोड सही दिखता है, लेकिन यह SOLID सिद्धांतों का उल्लंघन करता है। मान लीजिए कि हमें कार्ट मूल्य की गणना करते समय एक अलग प्रकार की वस्तु (जैसे कि किराना) के लिए एक नया नियम जोड़ने की आवश्यकता है। उस स्थिति में, हमें मूल वर्ग संशोधित करना होगा और उसके अंदर एक और शर्त लिखनी होगी जो कि की वस्तुओं की जांच करती है। Cart else if Grocery हालाँकि, थोड़ी रीफैक्टरिंग के साथ, हम कोड को खुले/बंद सिद्धांत का पालन करा सकते हैं। आइए देखें कैसे. सबसे पहले, हम वर्ग को अमूर्त बनाएंगे और विभिन्न प्रकार के के लिए ठोस वर्ग बनाएंगे जैसा कि नीचे दिखाया गया है। Item Item public abstract class Item { protected double price; public double getValue() { return price; } } public class ElectronicItem extends Item { public ElectronicItem(double price) { super.price = price; } @Override public double getValue() { return super.getValue()*1.11; } } public class GiftItem extends Item { public GiftItem(double price) { super.price = price; } @Override public double getValue() { return super.getValue()*1.08 + super.getValue()*0.02; } } public class GroceryItem extends Item { public GroceryItem(double price) { super.price = price; } @Override public double getValue() { return super.getValue()*1.03; } } प्रत्येक ठोस आइटम के अंदर , और जैसे वर्ग पद्धति को लागू करते हैं जिसमें कराधान और मूल्य गणना के लिए व्यावसायिक तर्क शामिल होते हैं। GroceryItem GiftItem ElectronicItem getValue() अब, हम क्लास को अमूर्त क्लास पर निर्भर बनाएंगे और नीचे दिखाए अनुसार प्रत्येक आइटम के लिए विधि लागू करेंगे। Cart Item getValue() import java.util.ArrayList; import java.util.List; public class Cart { private List<Item> items; public Cart(Payment paymentOption) { this.items = new ArrayList<>(); } public void addToCart(Item item) { items.add(item); } public double calculateCartValue() { double value = 0.0; for(Item item: items) { value += item.getValue(); } return value; } } अब, इस रिफैक्टर कोड में, भले ही नए प्रकार के पेश किए जाएं, क्लास अपरिवर्तित रहता है। बहुरूपता के कारण, के अंदर का वास्तविक प्रकार जो भी हो, उस वर्ग की विधि लागू की जाएगी। Item Cart items ArrayList Item getValue() 3. लिस्कोव का प्रतिस्थापन सिद्धांत लिस्कोव का प्रतिस्थापन सिद्धांत बताता है कि किसी दिए गए कोड में, भले ही हम सुपरक्लास के ऑब्जेक्ट को चाइल्ड क्लास के ऑब्जेक्ट से बदल दें, कोड टूटना नहीं चाहिए। दूसरे शब्दों में, जब एक उपवर्ग एक सुपरक्लास को विरासत में लेता है और उसके तरीकों को ओवरराइड करता है, तो उसे सुपरक्लास में विधि के व्यवहार के साथ स्थिरता बनाए रखनी चाहिए। उदाहरण के लिए, यदि हम निम्नलिखित वर्गों को और दो वर्गों को और वर्ग बनाते हैं। अब, मान लें कि हम वाहन वर्ग के भीतर नामक एक विधि बनाते हैं, इसे वर्ग में ओवरराइड किया जा सकता है, लेकिन यह वर्ग में असमर्थित होगा क्योंकि में इंजन नहीं है (नीचे कोड नमूना देखें) Vehicle Car Bicycle startEngine() Car Bicycle Bicycle तरीकों को ओवरराइड करते समय उपवर्ग को सुपरक्लास के व्यवहार के साथ एकरूपता बनाए रखनी चाहिए। class Vehicle { public void startEngine() { // start engine of the vehicle } } class Car extends Vehicle { @Override public void startEngine() { // Start Engine } } class Bicycle extends Vehicle { @Override public void startEngine(){ throw new UnsupportedOperationException("Bicycle doesn't have engine"); } } अब, मान लें कि कुछ कोड है जो वाहन प्रकार के ऑब्जेक्ट की अपेक्षा करता है और विधि पर निर्भर करता है। यदि, कोड के उस टुकड़े को कॉल करते समय प्रकार के ऑब्जेक्ट को पास करने के बजाय हम ऑब्जेक्ट को पास करते हैं, तो इससे कोड में समस्याएं पैदा होंगी। चूंकि विधि को कॉल करने पर वर्ग की विधि एक अपवाद फेंक देगी। यह SOLID सिद्धांतों (लिस्कोव के प्रतिस्थापन सिद्धांत) का उल्लंघन होगा startEngine() Vehicle Bicycle startEngine() Bicycle इस समस्या को हल करने के लिए, हम दो वर्ग और बना सकते हैं और क्लास से इनहेरिट कर सकते हैं और से इनहेरिट कर सकते हैं। MotorizedVehicle NonMotorizedVehicle Car MotorizedVehicle Bicycle NonMotorizedVehicle class Vehicle { } class MotorizedVehicle extends Vehicle { public void startEngine() { // start engine here } } class Car extends MotorizedVehicle { @Override public void startEngine() { // Start Engine } } class NonMotorizedVehicle extends Vehicle { public void startRiding() { // Start without engine } } class Bicycle extends NonMotorizedVehicle { @Override public void startRiding(){ // Start riding without the engine. } } 4. इंटरफ़ेस पृथक्करण सिद्धांत SOLID सिद्धांतों में "I" इंटरफ़ेस पृथक्करण सिद्धांत के लिए है। इंटरफ़ेस पृथक्करण सिद्धांत बताता है कि अप्रयुक्त तरीकों को लागू करने के लिए कक्षाओं को लागू करने के लिए मजबूर करने वाले बड़े इंटरफेस के बजाय, हमारे पास छोटे इंटरफेस होने चाहिए और कक्षाओं को लागू करना चाहिए। इस तरह, कक्षाएं केवल प्रासंगिक तरीकों को लागू करती हैं और स्वच्छ रहती हैं। अपने इंटरफ़ेस को एक बड़े इंटरफ़ेस के बजाय कई छोटे इंटरफ़ेस में विभाजित करें। उदाहरण के लिए, आइए जावा में अंतर्निहित संग्रह ढांचे को देखें। अन्य डेटा संरचनाओं के अलावा, जावा और डेटा संरचना भी प्रदान करता है, LinkedList ArrayList वर्ग निम्नलिखित इंटरफ़ेस लागू करता है: , , , , और । ArrayList Serializable Cloneable Iterable Collection List RandomAccess क्लास , , , , , और लागू करता है। LinkedList Serializable Cloneable Iterable Collection Deque List Queue यह काफी सारे इंटरफ़ेस हैं! इतने सारे इंटरफ़ेस रखने के बजाय, जावा डेवलपर्स , , , , और एक इंटरफ़ेस में जोड़ सकते थे, मान लें कि इंटरफ़ेस। अब, और दोनों वर्ग इस नए इंटरफ़ेस को लागू कर सकते थे। Serializable Cloneable Iterable Collecton List RandomAccess IList ArrayList LinkedList IList हालाँकि, चूँकि रैंडम एक्सेस का समर्थन नहीं करता है, इसलिए यह इंटरफ़ेस में विधियों को कार्यान्वित कर सकता था और जब कोई इसे कॉल करने का प्रयास करता है तो फेंक सकता था। LinkedList RandomAccess UnsupportedOperationException हालाँकि, यह SOLID सिद्धांतों में इंटरफ़ेस पृथक्करण सिद्धांत का उल्लंघन होगा क्योंकि यह लिंक्डलिस्ट क्लास को इंटरफ़ेस के अंदर तरीकों को लागू करने के लिए "मजबूर" करेगा, भले ही आवश्यकता न हो। RandomAccess इसलिए, सामान्य व्यवहार के आधार पर इंटरफ़ेस को विभाजित करना बेहतर है और प्रत्येक वर्ग को एक बड़े इंटरफ़ेस के बजाय कई इंटरफ़ेस लागू करने दें। निर्भरता व्युत्क्रम सिद्धांत निर्भरता व्युत्क्रम सिद्धांत कहता है कि ऊपरी स्तर की कक्षाओं को सीधे निचले स्तर की कक्षाओं पर निर्भर नहीं होना चाहिए। यह दो स्तरों के बीच एक तंग युग्मन का कारण बनता है। इसके बजाय, निचली कक्षाओं को एक इंटरफ़ेस प्रदान करना चाहिए जिस पर उच्च-स्तरीय कक्षाओं को निर्भर होना चाहिए। कक्षाओं के बजाय इंटरफेस पर निर्भर रहें उदाहरण के लिए, आइए ऊपर देखे गए उदाहरण को जारी रखें और कुछ भुगतान विकल्प जोड़ने के लिए इसे बढ़ाएं। आइए मान लें कि हमारे पास और दो प्रकार के भुगतान विकल्प हैं। अब, क्लास में, हम के लिए एक विधि जोड़ना चाहते हैं जो कार्ट मूल्य की गणना करेगी और आपूर्ति किए गए भुगतान के आधार पर भुगतान शुरू करेगी। तरीका। Cart DebitCard Paypal Cart placeOrder ऐसा करने के लिए, हम क्लास के अंदर फ़ील्ड के रूप में दो भुगतान विकल्पों को जोड़कर उपरोक्त उदाहरण में निर्भरता जोड़ सकते थे। हालाँकि, यह क्लास को और क्लास के साथ मजबूती से जोड़ देगा। Cart Cart Cart DebitCard Paypal इसके बजाय, हम एक इंटरफ़ेस बनाएंगे और और दोनों वर्गों में इंटरफ़ेस लागू करेंगे। अब, श्रेणी इंटरफ़ेस पर निर्भर करेगी, न कि व्यक्तिगत भुगतान प्रकारों पर। यह कक्षाओं को शिथिल रूप से जोड़े रखता है। Payment DebitCard Paypal Payment Cart Payment नीचे दिया गया कोड देखें. public interface Payment { void doPayment(double amount); } public class PaypalPayment implements Payment { @Override public void doPayment(double amount) { // logic to initiate paypal payment } } public class DebitCardPayment implements Payment { @Override public void doPayment(double amount) { // logic to initiate payment via debit card } } import java.util.ArrayList; import java.util.List; public class Cart { private List<Item> items; private Payment paymentOption; public Cart(Payment paymentOption) { this.items = new ArrayList<>(); this.paymentOption = paymentOption; } public void addToCart(Item item) { items.add(item); } public double calculateCartValue() { double value = 0.0; for(Item item: items) { value += item.getValue(); } return value; } public void placeOrder() { this.paymentOption.doPayment(calculateCartValue()); } } यदि आप ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग, GoF डिज़ाइन पैटर्न और निम्न-स्तरीय डिज़ाइन साक्षात्कार के बारे में अधिक जानने में रुचि रखते हैं, तो मेरे उच्च श्रेणी के पाठ्यक्रम अवश्य देखें। ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग + जावा डिज़ाइन पैटर्न को