এই ব্লগে, আমি একটি প্রতিক্রিয়া অ্যাপ্লিকেশনে সলিড নীতির বাস্তবায়ন প্রদর্শন করব। এই নিবন্ধের শেষে, আপনি সম্পূর্ণরূপে সলিড নীতিগুলি উপলব্ধি করতে পারবেন। আমরা শুরু করার আগে, আমি আপনাকে সেই নীতিগুলির একটি সংক্ষিপ্ত ভূমিকা দেব।
সলিড নীতি হল পাঁচটি নকশার নীতি যা আমাদের অ্যাপ্লিকেশনকে পুনঃব্যবহারযোগ্য, রক্ষণাবেক্ষণযোগ্য, মাপযোগ্য এবং ঢিলেঢালাভাবে সংযুক্ত রাখতে সাহায্য করে। সলিড নীতিগুলি হল:
ঠিক আছে, আসুন এই নীতিগুলির প্রতিটি পৃথকভাবে পরীক্ষা করি। আমি একটি উদাহরণ হিসাবে প্রতিক্রিয়া ব্যবহার করি, তবে মূল ধারণাগুলি অন্যান্য প্রোগ্রামিং ভাষা এবং কাঠামোর মতো।
"একটি মডিউল একজনের জন্য দায়ী হওয়া উচিত, এবং শুধুমাত্র একজন, অভিনেতা।" - উইকিপিডিয়া।
একক দায়িত্ব নীতি বলে যে একটি উপাদানের একটি স্পষ্ট উদ্দেশ্য বা দায়িত্ব থাকা উচিত। এটি নির্দিষ্ট কার্যকারিতা বা আচরণের উপর ফোকাস করা উচিত এবং সম্পর্কহীন কাজগুলি গ্রহণ করা এড়াতে হবে। SRP অনুসরণ করা উপাদানগুলিকে আরও মনোযোগী, মডুলার, সহজে বোধগম্য এবং পরিবর্তিত করে। এর বাস্তব বাস্তবায়ন দেখা যাক।
// ❌ 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
ফাংশন গ্রহণ করে এবং ফর্ম জমা দেওয়ার কাজ পরিচালনা করে। এই পদ্ধতির সাহায্যে আমরা উচ্চ-স্তরের উপাদান পরিবর্তন না করে সহজেই ফর্মের আচরণকে অদলবদল করতে বা প্রসারিত করতে পারি।
সলিড নীতিগুলি নির্দেশিকা প্রদান করে যা ডেভেলপারদেরকে সু-পরিকল্পিত, রক্ষণাবেক্ষণযোগ্য, এবং এক্সটেনসিবল সফ্টওয়্যার সমাধান তৈরি করতে সক্ষম করে। এই নীতিগুলি মেনে চলার মাধ্যমে, বিকাশকারীরা মডুলারিটি, কোড পুনঃব্যবহারযোগ্যতা, নমনীয়তা এবং কম কোড জটিলতা অর্জন করতে পারে।
আমি আশা করি এই ব্লগটি মূল্যবান অন্তর্দৃষ্টি প্রদান করেছে এবং আপনার বিদ্যমান বা অনুসরণকারী প্রতিক্রিয়া প্রকল্পগুলিতে এই নীতিগুলি প্রয়োগ করতে আপনাকে অনুপ্রাণিত করেছে৷
কৌতূহলী থাকুন; কোডিং রাখা!
তথ্যসূত্র:
এছাড়াও এখানে প্রকাশিত.