मोटे तौर पर, सभी प्रोग्रामिंग भाषाओं को दो प्रतिमानों में वर्गीकृत किया जा सकता है:
अनिवार्य / वस्तु-उन्मुख प्रोग्रामिंग - निर्देशों के अनुक्रम का पालन करता है जो पंक्ति-दर-पंक्ति निष्पादित होते हैं।
घोषणात्मक / कार्यात्मक प्रोग्रामिंग - अनुक्रमिक नहीं बल्कि कार्यक्रम के उद्देश्य के लिए खुद को अधिक चिंतित करता है। पूरा प्रोग्राम एक फंक्शन की तरह है जिसमें आगे सब-फंक्शन होते हैं, प्रत्येक एक निश्चित कार्य करता है।
एक जूनियर डेवलपर के रूप में, मैं इसे कठिन तरीके से महसूस कर रहा हूं (पढ़ें: मैं घबराहट से कोड की 1000 पंक्तियों को घूर रहा था) कि यह केवल कार्यात्मक कोड लिखने के बारे में नहीं है, बल्कि शब्दार्थ रूप से आसान और लचीला कोड भी है।
जबकि दोनों प्रतिमानों में स्वच्छ कोड लिखने के लिए कई सर्वोत्तम प्रथाएं हैं, मैं प्रोग्रामिंग के ऑब्जेक्ट-ओरिएंटेड प्रतिमान से संबंधित SOLID डिजाइन सिद्धांतों के बारे में बात करने वाला हूं।
एस - एकल जिम्मेदारी
ओ-ओपन-क्लोज्ड सिद्धांत
एल - लिस्कोव प्रतिस्थापन सिद्धांत
मैं - इंटरफ़ेस अलगाव
डी - निर्भरता उलटा
इन अवधारणाओं को समझने में किसी भी कठिनाई का मुख्य कारण यह नहीं है कि उनकी तकनीकी गहराई अथाह है, बल्कि इसलिए कि वे अमूर्त दिशानिर्देश हैं जो वस्तु-उन्मुख प्रोग्रामिंग में स्वच्छ कोड लिखने के लिए सामान्यीकृत हैं। आइए इन अवधारणाओं को समझने के लिए कुछ उच्च-स्तरीय वर्ग रेखाचित्र देखें।
ये सटीक क्लास डायग्राम नहीं हैं, लेकिन यह समझने में मदद करने के लिए बुनियादी ब्लूप्रिंट हैं कि क्लास में कौन से तरीके मौजूद हैं।
आइए पूरे लेख में एक कैफे के उदाहरण पर विचार करें।
एक वर्ग के पास बदलने का केवल एक ही कारण होना चाहिए
इस वर्ग पर विचार करें जो कैफे द्वारा प्राप्त ऑनलाइन ऑर्डर को संभालता है।
इसके साथ गलत क्या है?
यह एकल वर्ग कई कार्यों के लिए उत्तरदायी है। यदि आपको अन्य भुगतान विधियों को जोड़ना पड़े तो क्या होगा? क्या होगा यदि आपके पास पुष्टिकरण भेजने के कई तरीके हैं? प्रसंस्करण आदेश के लिए ज़िम्मेदार वर्ग में भुगतान के तर्क को बदलना बहुत अच्छा डिज़ाइन नहीं है। यह अत्यधिक गैर-लचीले कोड की ओर जाता है।
एक बेहतर तरीका यह होगा कि इन विशिष्ट कार्यात्मकताओं को ठोस वर्गों में अलग कर दिया जाए और उनका एक उदाहरण कॉल किया जाए, जैसा कि नीचे दिए गए उदाहरण में दिखाया गया है।
इकाइयां विस्तार के लिए खुली होनी चाहिए लेकिन संशोधन के लिए बंद होनी चाहिए।
कैफे में, आपको विकल्पों की सूची में से अपनी कॉफी के लिए मसालों का चयन करना होता है और एक वर्ग होता है जो इसे संभालता है।
कैफे ने एक नया मसाला, मक्खन जोड़ने का फैसला किया। ध्यान दें कि चयनित मसालों के अनुसार मूल्य कैसे बदलेगा और मूल्य गणना का तर्क कॉफी वर्ग में है। न केवल हमें हर बार एक नया मसाला वर्ग जोड़ना पड़ता है जो मुख्य वर्ग में संभावित कोड परिवर्तन बनाता है, बल्कि तर्क को हर बार अलग तरीके से संभालता है।
एक बेहतर तरीका यह होगा कि एक मसाला इंटरफ़ेस बनाया जाए जो इसके तरीकों को ओवरराइड करने वाले चाइल्ड क्लास को बदल सके। और मुख्य वर्ग मापदंडों को पारित करने और प्रत्येक आदेश के लिए मात्रा और मूल्य प्राप्त करने के लिए बस मसालों के इंटरफ़ेस का उपयोग कर सकता है।
इसके दो फायदे हैं:
1. आप अलग-अलग या कई मसालों के लिए अपने ऑर्डर को गतिशील रूप से बदल सकते हैं (मोचा और चॉकलेट के साथ कॉफी स्वर्गीय लगता है)।
2. कॉन्डिमेंट्स क्लास का कॉफी क्लास के साथ हैस-ए संबंध होने वाला है, न कि आईएस-ए। तो आपकी कॉफी में आपकी कॉफी के बजाय मोचा/मक्खन/दूध हो सकता है-एक मोचा/मक्खन/दूध प्रकार की कॉफी।
प्रत्येक उपवर्ग या व्युत्पन्न वर्ग को उसके आधार या मूल वर्ग के लिए प्रतिस्थापन योग्य होना चाहिए।
इसका मतलब यह है कि उप-वर्ग सीधे मूल वर्ग को बदलने में सक्षम होना चाहिए; इसकी समान कार्यक्षमता होनी चाहिए। मुझे इसे समझने में मुश्किल हुई क्योंकि यह कुछ जटिल गणित सूत्र जैसा लगता है। लेकिन मैं इस लेख में इसे स्पष्ट करने की कोशिश करूंगा।
कैफे में कर्मचारियों पर विचार करें। बरिस्ता, प्रबंधक और सर्वर हैं। उन सभी की समान कार्यक्षमता है।
इसलिए हम नाम, स्थिति, गेटनेम, गेटपोस्टियन, टेकऑर्डर (), सर्व () के साथ एक बेस स्टाफ क्लास बना सकते हैं।
प्रत्येक ठोस वर्ग, वेटर, बरिस्ता और प्रबंधक इससे प्राप्त कर सकते हैं और स्थिति के लिए आवश्यकतानुसार उन्हें लागू करने के लिए समान विधियों को ओवरराइड कर सकते हैं।
इस उदाहरण में, लिस्कोव प्रतिस्थापन सिद्धांत (LSP) का उपयोग यह सुनिश्चित करने के लिए किया जाता है कि कोड की शुद्धता को प्रभावित किए बिना स्टाफ के किसी भी व्युत्पन्न वर्ग को बेस स्टाफ वर्ग के साथ परस्पर उपयोग किया जा सकता है।
उदाहरण के लिए, वेटर वर्ग स्टाफ़ वर्ग का विस्तार करता है और वेटर की भूमिका के लिए विशिष्ट अतिरिक्त कार्यक्षमता को शामिल करने के लिए टेकऑर्डर और सर्वऑर्डर विधियों को ओवरराइड करता है। हालाँकि, इससे भी महत्वपूर्ण बात यह है कि कार्यक्षमता में अंतर के बावजूद, कोई भी कोड जो स्टाफ क्लास के ऑब्जेक्ट की अपेक्षा करता है, वेटर क्लास के ऑब्जेक्ट के साथ भी सही तरीके से काम कर सकता है।
public class Cafe { public void serveCustomer (Staff staff) { staff.takeOrder(); staff.serveOrder(); } } public class Main { public static void main (String[] args) { Cafe cafe = new Cafe(); Staff staff1 = new Staff( "John" , "Staff" ); Waiter waiter1 = new Waiter( "Jane" ); restaurant.serveCustomer(staff1); // Works correctly with Staff object
restaurant.serveCustomer(waiter1); // Works correctly with Waiter object
} }
यहां वर्ग कैफे में विधि सेवा ग्राहक () एक स्टाफ ऑब्जेक्ट को एक पैरामीटर के रूप में लेती है। सर्वकस्टमर () विधि ग्राहक की सेवा के लिए स्टाफ़ ऑब्जेक्ट के टेकऑर्डर () और सर्वऑर्डर () विधियों को कॉल करती है।
मेन क्लास में, हम एक स्टाफ़ ऑब्जेक्ट और एक वेटर ऑब्जेक्ट बनाते हैं। फिर हम कैफ़े क्लास के सर्व कस्टमर () मेथड को दो बार कॉल करते हैं - एक बार स्टाफ़ ऑब्जेक्ट के साथ और एक बार वेटर ऑब्जेक्ट के साथ।
चूँकि वेटर क्लास स्टाफ़ क्लास से ली गई है, कोई भी कोड जो स्टाफ़ क्लास के ऑब्जेक्ट की अपेक्षा करता है, वेटर क्लास के ऑब्जेक्ट के साथ भी सही तरीके से काम कर सकता है। इस मामले में, कैफे वर्ग की सेवा ग्राहक () विधि स्टाफ ऑब्जेक्ट और वेटर ऑब्जेक्ट दोनों के साथ सही ढंग से काम करती है, भले ही वेटर ऑब्जेक्ट में वेटर की भूमिका के लिए अतिरिक्त कार्यक्षमता हो।
कक्षाओं को उन विधियों पर निर्भर होने के लिए मजबूर नहीं किया जाना चाहिए जिनका वे उपयोग नहीं करते हैं।
तो कैफे में यह बहुत ही बहुमुखी वेंडिंग मशीन है जो कॉफी, चाय, स्नैक्स और सोडा दे सकती है।
इसमें गलत क्या है? तकनीकी रूप से कुछ भी नहीं। यदि आपको कॉफी वितरण जैसे किसी भी कार्य के लिए इंटरफ़ेस लागू करना है, तो आपको चाय, सोडा और स्नैक्स के लिए अन्य तरीकों को भी लागू करने की आवश्यकता है। यह अनावश्यक है और ये कार्य एक दूसरे की कार्यक्षमता से संबंधित नहीं हैं। उनमें से प्रत्येक कार्य में उनके बीच बहुत कम सामंजस्य है।
सामंजस्य क्या है? यह एक कारक है जो यह निर्धारित करता है कि एक दूसरे से संबंधित इंटरफ़ेस में विधियां कितनी दृढ़ता से हैं।
और वेंडिंग मशीन के मामले में, तरीके शायद ही अन्योन्याश्रित हैं। हम तरीकों को अलग कर सकते हैं क्योंकि उनमें बहुत कम सामंजस्य है।
अब, कोई भी इंटरफ़ेस जो एक चीज़ को लागू करने के लिए है, उसे केवल टेकमनी () लागू करना चाहिए जो सभी कार्यों के लिए सामान्य है। यह एक इंटरफ़ेस में असंबंधित कार्यों को अलग करता है इसलिए एक इंटरफ़ेस में असंबंधित कार्यों को जबरदस्ती लागू करने से परहेज करता है।
उच्च स्तरीय मॉड्यूल निम्न स्तर के मॉड्यूल पर निर्भर नहीं होना चाहिए। विवरण सार पर निर्भर होना चाहिए।
कैफे में एयर कंडीशनर (कूलर) पर विचार करें। और अगर आप मेरी तरह हैं, तो वहां हमेशा ठंड रहती है। आइए रिमोट कंट्रोल और एसी कक्षाओं को देखें।
यहां, रिमोटकंट्रोल उच्च स्तरीय मॉड्यूल है जो निम्न स्तर के घटक एसी पर निर्भर करता है। अगर मुझे वोट मिलता है, तो मुझे एक हीटर भी चाहिए: पी तो सामान्य तापमान विनियमन के लिए, कूलर के बजाय, हम रिमोट और तापमान नियंत्रण को अलग कर दें। लेकिन रिमोट कंट्रोल क्लास को एसी के साथ कसकर जोड़ा जाता है, जो एक ठोस कार्यान्वयन है। निर्भरता को कम करने के लिए, हम एक इंटरफ़ेस बना सकते हैं जिसमें 45-65 एफ की सीमा के भीतर केवल वृद्धि टेम्प () और घटते टेम्प () के कार्य हैं।
जैसा कि आप देख सकते हैं, उच्च-स्तरीय और निम्न-स्तरीय दोनों मॉड्यूल एक इंटरफ़ेस पर निर्भर करते हैं जो बढ़ते या घटते तापमान की कार्यक्षमता को अमूर्त करता है।
कंक्रीट वर्ग, एसी, लागू तापमान सीमा के साथ विधियों को लागू करता है।
अब मैं शायद उस हीटर को प्राप्त कर सकता हूं जिसे मैं हीटर नामक एक अलग कंक्रीट वर्ग में अलग-अलग तापमान रेंज लागू करना चाहता हूं।
उच्च-स्तरीय मॉड्यूल, रिमोटकंट्रोल को केवल रन टाइम के दौरान सही विधि को कॉल करने की चिंता करनी है।