"एक मॉड्यूल को केवल एक, और केवल एक अभिनेता के प्रति जिम्मेदार होना चाहिए।" — विकिपीडिया.
// ❌ 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
फ़ंक्शन को एक प्रोप के रूप में प्राप्त करता है और फॉर्म सबमिशन को संभालता है। यह दृष्टिकोण हमें उच्च-स्तरीय घटक को संशोधित किए बिना फॉर्म व्यवहार को आसानी से स्वैप करने या विस्तारित करने की अनुमति देता है।
जिज्ञासु बने; कोडिंग करते रहें!
संदर्भ:
भी प्रकाशित किया गया है.