"একটি মডিউল একজনের জন্য দায়ী হওয়া উচিত, এবং শুধুমাত্র একজন, অভিনেতা।" - উইকিপিডিয়া।
// ❌ 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
উপাদান একাধিক দায়িত্ব গ্রহণ করে একক দায়িত্ব নীতি লঙ্ঘন করে। এটি পণ্যের পুনরাবৃত্তি পরিচালনা করে এবং প্রতিটি পণ্যের জন্য UI রেন্ডারিং পরিচালনা করে। এটি ভবিষ্যতে উপাদানটিকে বোঝা, বজায় রাখা এবং পরীক্ষা করা চ্যালেঞ্জিং করে তুলতে পারে।
// ✅ 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
উপাদান রয়েছে যা প্রতিক্রিয়াতে একটি কাস্টম নির্বাচন ইনপুট হিসাবে পরিবেশন করার উদ্দেশ্যে। যাইহোক, এটি লিসকভ সাবস্টিটিউশন প্রিন্সিপল (LSP) লঙ্ঘন করে কারণ এটি বেস 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
কম্পোনেন্ট রয়েছে যা React-এ স্ট্যান্ডার্ড select
এলিমেন্টের কার্যকারিতা বাড়ানোর উদ্দেশ্যে। উপাদানটি স্প্রেড অপারেটর ...props
ব্যবহার করে value
, iconClassName
, handleChange
, এবং অতিরিক্ত প্রপগুলির মতো প্রপগুলি গ্রহণ করে৷ select
উপাদানের বৈশিষ্ট্য ব্যবহার করার অনুমতি দিয়ে এবং অতিরিক্ত প্রপস গ্রহণ করে, CustomSelect
উপাদানটি Liskov সাবস্টিটিউশন প্রিন্সিপল (LSP) অনুসরণ করে।
"কোনও কোড ব্যবহার করে না এমন পদ্ধতির উপর নির্ভর করতে বাধ্য করা উচিত নয়।" - উইকিপিডিয়া।
// ❌ 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 Product = ({ 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
কম্পোনেন্টে পাস করি, যদিও এটির প্রয়োজন নেই। এটি উপাদানটিতে অপ্রয়োজনীয় ঝুঁকি এবং জটিলতা যুক্ত করে এবং ইন্টারফেস সেগ্রিগেশন প্রিন্সিপল (ISP) লঙ্ঘন করে।
// ✅ 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 Product = ({ 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
উপাদানটি সম্পূর্ণ পণ্যের বিবরণের পরিবর্তে শুধুমাত্র প্রয়োজনীয় তথ্য পায়। এটি অপ্রয়োজনীয় ঝুঁকি প্রতিরোধ করে এবং ইন্টারফেস সেগ্রিগেশন প্রিন্সিপল (ISP) কে উৎসাহিত করে।
"একটি সত্তাকে বিমূর্ততার উপর নির্ভর করতে হবে, সংকীর্ণতার উপর নয়" - উইকিপিডিয়া।
// ❌ 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
ফাংশন গ্রহণ করে এবং ফর্ম জমা দেওয়ার কাজ পরিচালনা করে। এই পদ্ধতির সাহায্যে আমরা উচ্চ-স্তরের উপাদান পরিবর্তন না করে সহজেই ফর্মের আচরণকে অদলবদল করতে বা প্রসারিত করতে পারি।
কৌতূহলী থাকুন; কোডিং রাখা!
তথ্যসূত্র:
এছাড়াও প্রকাশিত.