इस ब्लॉग में, मैं एक रिएक्ट एप्लिकेशन में SOLID सिद्धांतों के कार्यान्वयन का प्रदर्शन करूंगा। इस लेख के अंत तक, आप SOLID सिद्धांतों को पूरी तरह से समझ जाएंगे। शुरू करने से पहले, मैं आपको उन सिद्धांतों का संक्षिप्त परिचय देता हूँ।
SOLID सिद्धांत पाँच डिज़ाइन सिद्धांत हैं जो हमें अपने एप्लिकेशन को पुन: प्रयोज्य, रखरखाव योग्य, स्केलेबल और शिथिल रूप से युग्मित रखने में मदद करते हैं। ठोस सिद्धांत हैं:
ठीक है, आइए इनमें से प्रत्येक सिद्धांत की व्यक्तिगत रूप से जाँच करें। मैं एक उदाहरण के रूप में रिएक्ट का उपयोग करता हूं, लेकिन मूल अवधारणाएं अन्य प्रोग्रामिंग भाषाओं और फ्रेमवर्क के समान हैं।
"एक मॉड्यूल को केवल एक, और केवल एक अभिनेता के प्रति जिम्मेदार होना चाहिए।" — विकिपीडिया.
एकल उत्तरदायित्व सिद्धांत कहता है कि एक घटक का एक स्पष्ट उद्देश्य या जिम्मेदारी होनी चाहिए। इसे विशिष्ट कार्यक्षमता या व्यवहार पर ध्यान केंद्रित करना चाहिए और असंबंधित कार्यों को करने से बचना चाहिए। एसआरपी का पालन करने से घटक अधिक केंद्रित, मॉड्यूलर और आसानी से समझे और संशोधित हो जाते हैं। आइए वास्तविक कार्यान्वयन देखें।
// ❌ Bad Practice: Component with Multiple Responsibilities const Products = () => { return ( <div className="products"> {products.map((product) => ( <div key={product?.id} className="product"> <h3>{product?.name}</h3> <p>${product?.price}</p> </div> ))} </div> ); };
उपरोक्त उदाहरण में, Products
घटक कई जिम्मेदारियाँ लेकर एकल जिम्मेदारी सिद्धांत का उल्लंघन करता है। यह उत्पादों की पुनरावृत्ति का प्रबंधन करता है और प्रत्येक उत्पाद के लिए यूआई रेंडरिंग को संभालता है। इससे भविष्य में घटक को समझना, बनाए रखना और परीक्षण करना चुनौतीपूर्ण हो सकता है।
// ✅ Good Practice: Separating Responsibilities into Smaller Components import Product from './Product'; import products from '../../data/products.json'; const Products = () => { return ( <div className="products"> {products.map((product) => ( <Product key={product?.id} product={product} /> ))} </div> ); }; // Product.js // Separate component responsible for rendering the product details const Product = ({ product }) => { return ( <div className="product"> <h3>{product?.name}</h3> <p>${product?.price}</p> </div> ); };
यह पृथक्करण सुनिश्चित करता है कि प्रत्येक घटक की एक ही ज़िम्मेदारी हो, जिससे उन्हें समझना, परीक्षण करना और बनाए रखना आसान हो जाता है।
"सॉफ्टवेयर इकाइयां (कक्षाएं, मॉड्यूल, फ़ंक्शन इत्यादि) विस्तार के लिए खुली होनी चाहिए, लेकिन संशोधन के लिए बंद होनी चाहिए।" — विकिपीडिया.
खुला-बंद सिद्धांत इस बात पर जोर देता है कि घटक विस्तार के लिए खुले होने चाहिए (नए व्यवहार या कार्यक्षमताएँ जोड़ सकते हैं) लेकिन संशोधन के लिए बंद होने चाहिए (मौजूदा कोड अपरिवर्तित रहना चाहिए)। यह सिद्धांत ऐसे कोड के निर्माण को प्रोत्साहित करता है जो परिवर्तन के लिए लचीला, मॉड्यूलर और आसानी से बनाए रखने योग्य हो। आइए वास्तविक कार्यान्वयन देखें।
// ❌ Bad Practice: Violating the Open-Closed Principle // Button.js // Existing Button component const Button = ({ text, onClick }) => { return ( <button onClick={onClick}> {text} </button> ); } // Button.js // Modified Existing Button component with additional icon prop (modification) const Button = ({ text, onClick, icon }) => { return ( <button onClick={onClick}> <i className={icon} /> <span>{text}</span> </button> ); } // Home.js // 👇 Avoid: Modified existing component prop const Home = () => { const handleClick= () => {}; return ( <div> {/* ❌ Avoid this */} <Button text="Submit" onClick={handleClick} icon="fas fa-arrow-right" /> </div> ); }
उपरोक्त उदाहरण में, हम एक icon
प्रोप जोड़कर मौजूदा Button
घटक को संशोधित करते हैं। नई आवश्यकताओं को समायोजित करने के लिए मौजूदा घटक को बदलना ओपन-क्लोज्ड सिद्धांत का उल्लंघन करता है। ये परिवर्तन घटक को अधिक नाजुक बनाते हैं और विभिन्न संदर्भों में उपयोग किए जाने पर अनपेक्षित दुष्प्रभावों का जोखिम पैदा करते हैं।
// ✅ Good Practice: Open-Closed Principle // Button.js // Existing Button functional component const Button = ({ text, onClick }) => { return ( <button onClick={onClick}> {text} </button> ); } // IconButton.js // IconButton component // ✅ Good: You have not modified anything here. const IconButton = ({ text, icon, onClick }) => { return ( <button onClick={onClick}> <i className={icon} /> <span>{text}</span> </button> ); } const Home = () => { const handleClick = () => { // Handle button click event } return ( <div> <Button text="Submit" onClick={handleClick} /> {/* <IconButton text="Submit" icon="fas fa-heart" onClick={handleClick} /> </div> ); }
उपरोक्त उदाहरण में, हम एक अलग IconButton
कार्यात्मक घटक बनाते हैं। IconButton
घटक मौजूदा Button
घटक को संशोधित किए बिना एक आइकन बटन के प्रतिपादन को समाहित करता है। यह संशोधन के बजाय संरचना के माध्यम से कार्यक्षमता का विस्तार करके खुले-बंद सिद्धांत का पालन करता है।
"उपप्रकार की वस्तुएं सुपरटाइप वस्तुओं के लिए प्रतिस्थापन योग्य होनी चाहिए" - विकिपीडिया।
लिस्कोव प्रतिस्थापन सिद्धांत (एलएसपी) ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग का एक मौलिक सिद्धांत है जो पदानुक्रम के भीतर वस्तुओं की प्रतिस्थापन क्षमता की आवश्यकता पर जोर देता है। रिएक्ट घटकों के संदर्भ में, एलएसपी इस विचार को बढ़ावा देता है कि व्युत्पन्न घटकों को एप्लिकेशन की शुद्धता या व्यवहार को प्रभावित किए बिना अपने आधार घटकों को प्रतिस्थापित करने में सक्षम होना चाहिए। आइए वास्तविक कार्यान्वयन देखें।
// ⚠️ Bad Practice // This approach violates the Liskov Substitution Principle as it modifies // the behavior of the derived component, potentially resulting in unforeseen // problems when substituting it for the base Select component. const BadCustomSelect = ({ value, iconClassName, handleChange }) => { return ( <div> <i className={iconClassName}></i> <select value={value} onChange={handleChange}> <options value={1}>One</options> <options value={2}>Two</options> <options value={3}>Three</options> </select> </div> ); }; const LiskovSubstitutionPrinciple = () => { const [value, setValue] = useState(1); const handleChange = (event) => { setValue(event.target.value); }; return ( <div> {/** ❌ Avoid this */} {/** Below Custom Select doesn't have the characteristics of base `select` element */} <BadCustomSelect value={value} handleChange={handleChange} /> </div> );
उपरोक्त उदाहरण में, हमारे पास एक BadCustomSelect
घटक है जिसका उद्देश्य रिएक्ट में एक कस्टम चयन इनपुट के रूप में काम करना है। हालाँकि, यह लिस्कोव प्रतिस्थापन सिद्धांत (एलएसपी) का उल्लंघन करता है क्योंकि यह आधार select
तत्व के व्यवहार को प्रतिबंधित करता है।
// ✅ Good Practice // This component follows the Liskov Substitution Principle and allows the use of select's characteristics. const CustomSelect = ({ value, iconClassName, handleChange, ...props }) => { return ( <div> <i className={iconClassName}></i> <select value={value} onChange={handleChange} {...props}> <options value={1}>One</options> <options value={2}>Two</options> <options value={3}>Three</options> </select> </div> ); }; const LiskovSubstitutionPrinciple = () => { const [value, setValue] = useState(1); const handleChange = (event) => { setValue(event.target.value); }; return ( <div> {/* ✅ This CustomSelect component follows the Liskov Substitution Principle */} <CustomSelect value={value} handleChange={handleChange} defaultValue={1} /> </div> ); };
संशोधित कोड में, हमारे पास एक CustomSelect
घटक है जिसका उद्देश्य रिएक्ट में मानक select
तत्व की कार्यक्षमता को बढ़ाना है। घटक स्प्रेड ऑपरेटर ...props
का उपयोग करके value
, iconClassName
, handleChange
और अतिरिक्त प्रॉप्स जैसे प्रॉप्स को स्वीकार करता है। select
तत्व की विशेषताओं के उपयोग की अनुमति देकर और अतिरिक्त प्रॉप्स को स्वीकार करके, CustomSelect
घटक लिस्कोव प्रतिस्थापन सिद्धांत (एलएसपी) का पालन करता है।
"किसी भी कोड को उन तरीकों पर निर्भर होने के लिए मजबूर नहीं किया जाना चाहिए जिनका वह उपयोग नहीं करता है।" — विकिपीडिया.
इंटरफ़ेस पृथक्करण सिद्धांत (आईएसपी) सुझाव देता है कि इंटरफ़ेस को अत्यधिक व्यापक होने और ग्राहकों को अनावश्यक कार्यक्षमता लागू करने के लिए मजबूर करने के बजाय विशिष्ट ग्राहक आवश्यकताओं पर केंद्रित और अनुकूलित किया जाना चाहिए। आइए वास्तविक कार्यान्वयन देखें।
// ❌ Avoid: disclose unnecessary information for this component // This introduces unnecessary dependencies and complexity for the component const ProductThumbnailURL = ({ product }) => { return ( <div> <img src={product.imageURL} alt={product.name} /> </div> ); }; // ❌ Bad Practice const Products = ({ product }) => { return ( <div> <ProductThumbnailURL product={product} /> <h4>{product?.name}</h4> <p>{product?.description}</p> <p>{product?.price}</p> </div> ); }; const Products = () => { return ( <div> {products.map((product) => ( <Product key={product.id} product={product} /> ))} </div> ); }
उपरोक्त उदाहरण में, हम संपूर्ण उत्पाद विवरण ProductThumbnailURL
घटक को पास करते हैं, भले ही उसे इसकी आवश्यकता नहीं है। यह घटक में अनावश्यक जोखिम और जटिलता जोड़ता है और इंटरफ़ेस पृथक्करण सिद्धांत (आईएसपी) का उल्लंघन करता है।
// ✅ Good: reducing unnecessary dependencies and making // the codebase more maintainable and scalable. const ProductThumbnailURL = ({ imageURL, alt }) => { return ( <div> <img src={imageURL} alt={alt} /> </div> ); }; // ✅ Good Practice const Products = ({ product }) => { return ( <div> <ProductThumbnailURL imageURL={product.imageURL} alt={product.name} /> <h4>{product?.name}</h4> <p>{product?.description}</p> <p>{product?.price}</p> </div> ); }; const Products = () => { return ( <div> {products.map((product) => ( <Product key={product.id} product={product} /> ))} </div> ); };
संशोधित कोड में, ProductThumbnailURL
घटक संपूर्ण उत्पाद विवरण के बजाय केवल आवश्यक जानकारी प्राप्त करता है। यह अनावश्यक जोखिमों को रोकता है और इंटरफ़ेस पृथक्करण सिद्धांत (आईएसपी) को बढ़ावा देता है।
"एक इकाई को अमूर्त पर निर्भर होना चाहिए, ठोस पर नहीं" - विकिपीडिया।
निर्भरता व्युत्क्रम सिद्धांत (डीआईपी) इस बात पर जोर देता है कि उच्च-स्तरीय घटकों को निम्न-स्तरीय घटकों पर निर्भर नहीं होना चाहिए। यह सिद्धांत ढीले युग्मन और मॉड्यूलरिटी को बढ़ावा देता है और सॉफ्टवेयर सिस्टम के आसान रखरखाव की सुविधा प्रदान करता है। आइए वास्तविक कार्यान्वयन देखें।
// ❌ Bad Practice // This component follows concretion instead of abstraction and // breaks Dependency Inversion Principle const CustomForm = ({ children }) => { const handleSubmit = () => { // submit operations }; return <form onSubmit={handleSubmit}>{children}</form>; }; const DependencyInversionPrinciple = () => { const [email, setEmail] = useState(); const handleChange = (event) => { setEmail(event.target.value); }; const handleFormSubmit = (event) => { // submit business logic here }; return ( <div> {/** ❌ Avoid: tightly coupled and hard to change */} <BadCustomForm> <input type="email" value={email} onChange={handleChange} name="email" /> </BadCustomForm> </div> ); };
CustomForm
घटक अपने बच्चों के साथ कसकर जुड़ा हुआ है, लचीलेपन को रोकता है और इसके व्यवहार को बदलना या विस्तारित करना चुनौतीपूर्ण बनाता है।
// ✅ Good Practice // This component follows abstraction and promotes Dependency Inversion Principle const AbstractForm = ({ children, onSubmit }) => { const handleSubmit = (event) => { event.preventDefault(); onSubmit(); }; return <form onSubmit={handleSubmit}>{children}</form>; }; const DependencyInversionPrinciple = () => { const [email, setEmail] = useState(); const handleChange = (event) => { setEmail(event.target.value); }; const handleFormSubmit = () => { // submit business logic here }; return ( <div> {/** ✅ Use the abstraction instead */} <AbstractForm onSubmit={handleFormSubmit}> <input type="email" value={email} onChange={handleChange} name="email" /> <button type="submit">Submit</button> </AbstractForm> </div> ); };
संशोधित कोड में, हम AbstractForm
घटक का परिचय देते हैं, जो फॉर्म के लिए एक अमूर्त के रूप में कार्य करता है। यह onSubmit
फ़ंक्शन को एक प्रोप के रूप में प्राप्त करता है और फॉर्म सबमिशन को संभालता है। यह दृष्टिकोण हमें उच्च-स्तरीय घटक को संशोधित किए बिना फॉर्म व्यवहार को आसानी से स्वैप करने या विस्तारित करने की अनुमति देता है।
SOLID सिद्धांत दिशानिर्देश प्रदान करते हैं जो डेवलपर्स को अच्छी तरह से डिज़ाइन किए गए, रखरखाव योग्य और एक्स्टेंसिबल सॉफ़्टवेयर समाधान बनाने के लिए सशक्त बनाते हैं। इन सिद्धांतों का पालन करके, डेवलपर्स मॉड्यूलरिटी, कोड पुन: प्रयोज्यता, लचीलापन और कम कोड जटिलता प्राप्त कर सकते हैं।
मुझे आशा है कि इस ब्लॉग ने बहुमूल्य अंतर्दृष्टि प्रदान की है और आपको इन सिद्धांतों को अपने मौजूदा या निम्नलिखित रिएक्ट प्रोजेक्ट्स में लागू करने के लिए प्रेरित किया है।
जिज्ञासु बने; कोडिंग करते रहें!
संदर्भ:
यहाँ भी प्रकाशित किया गया है.