paint-brush
Sadece 8 Dakikada Avucunuzun içi Gibi Sağlam Prensiplerde Ustalaşın!ile@arulvalananto
26,411 okumalar
26,411 okumalar

Sadece 8 Dakikada Avucunuzun içi Gibi Sağlam Prensiplerde Ustalaşın!

ile Arul Valan Anto11m2023/07/07
Read on Terminal Reader
Read this story w/o Javascript

Çok uzun; Okumak

Bu blogda SOLID ilkelerinin bir React uygulamasında uygulanmasını göstereceğim. Bu makalenin sonunda SOLID ilkelerini tam olarak kavrayacaksınız.
featured image - Sadece 8 Dakikada Avucunuzun içi Gibi Sağlam Prensiplerde Ustalaşın!
Arul Valan Anto HackerNoon profile picture
0-item

Bu blogda SOLID ilkelerinin bir React uygulamasında uygulanmasını göstereceğim. Bu makalenin sonunda SOLID ilkelerini tam olarak kavrayacaksınız. Başlamadan önce, size bu ilkelerle ilgili kısa bir giriş yapmama izin verin.

SOLID ilkeleri nelerdir?

SOLID ilkeleri, uygulamamızı yeniden kullanılabilir, bakımı yapılabilir, ölçeklenebilir ve gevşek bağlı tutmamıza yardımcı olan beş tasarım ilkesidir. SOLID ilkeleri şunlardır:


  • Tek sorumluluk ilkesi
  • Açık-Kapalı prensibi
  • Liskov ikame ilkesi
  • Arayüz ayırma ilkesi
  • Bağımlılığı tersine çevirme ilkesi


Tamam, gelin bu ilkelerin her birini ayrı ayrı inceleyelim. Örnek olarak React'ı kullanıyorum ancak temel kavramlar diğer programlama dilleri ve çerçevelerine benzer.

Tek sorumluluk ilkesi

“Bir modül tek ve tek bir aktöre karşı sorumlu olmalıdır.” — Vikipedi.


Tek Sorumluluk İlkesi, bir bileşenin net bir amacı veya sorumluluğu olması gerektiğini belirtir. Belirli işlevsellik veya davranışlara odaklanmalı ve ilgisiz görevleri üstlenmekten kaçınmalıdır. SRP'yi takip etmek bileşenleri daha odaklı, modüler, kolay anlaşılır ve değiştirilebilir hale getirir. Gerçek uygulamaya bakalım.


 // ❌ 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> ); };


Yukarıdaki örnekte Products bileşeni, birden fazla sorumluluk üstlenerek Tek Sorumluluk İlkesini ihlal etmektedir. Ürünlerin yinelenmesini yönetir ve her ürün için kullanıcı arayüzü oluşturma işlemini gerçekleştirir. Bu, bileşenin gelecekte anlaşılmasını, bakımını ve test edilmesini zorlaştırabilir.


Bunun yerine SRP'ye uymak için şunu yapın:

 // ✅ 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> ); };


Bu ayırma, her bileşenin tek bir sorumluluğa sahip olmasını sağlayarak bunların anlaşılmasını, test edilmesini ve bakımını kolaylaştırır.

Açık-Kapalı Prensibi

“Yazılım varlıkları (sınıflar, modüller, işlevler vb.) genişletmeye açık, değişiklik yapmaya kapalı olmalıdır.” — Vikipedi.


Açık-Kapalı Prensibi, bileşenlerin genişlemeye açık (yeni davranışlar veya işlevler eklenebilir) ancak değişiklik için kapalı olması (mevcut kod değişmeden kalmalıdır) gerektiğini vurgular. Bu ilke, değişime dayanıklı, modüler ve bakımı kolay kodun oluşturulmasını teşvik eder. Gerçek uygulamaya bakalım.


 // ❌ 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> ); }


Yukarıdaki örnekte, mevcut Button bileşenini bir icon prop ekleyerek değiştiriyoruz. Mevcut bir bileşeni yeni gereksinimleri karşılayacak şekilde değiştirmek Açık-Kapalı İlkesini ihlal eder. Bu değişiklikler bileşeni daha kırılgan hale getirir ve farklı bağlamlarda kullanıldığında istenmeyen yan etki riskini ortaya çıkarır.


Bunun yerine şunu yapın:

 // ✅ 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> ); }


Yukarıdaki örnekte ayrı bir IconButton fonksiyonel bileşeni oluşturuyoruz. IconButton bileşeni, mevcut Button bileşenini değiştirmeden bir simge düğmesinin oluşturulmasını kapsar. İşlevselliği değişiklik yerine kompozisyon yoluyla genişleterek Açık-Kapalı Prensibine bağlı kalır.


Liskov Değiştirme Prensibi

"Alt tip nesneler, süper tip nesnelerin yerine geçebilmelidir" - Vikipedi.


Liskov Değiştirme İlkesi (LSP), bir hiyerarşi içindeki nesnelerin ikame edilebilirliği ihtiyacını vurgulayan, nesne yönelimli programlamanın temel bir ilkesidir. React bileşenleri bağlamında LSP, türetilmiş bileşenlerin, uygulamanın doğruluğunu veya davranışını etkilemeden temel bileşenlerinin yerini alabilmesi gerektiği fikrini destekler. Gerçek uygulamaya bakalım.


 // ⚠️ 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> );


Yukarıdaki örnekte, React'te özel seçim girişi olarak hizmet etmesi amaçlanan bir BadCustomSelect bileşenimiz var. Ancak temel select öğesinin davranışını kısıtladığı için Liskov Değiştirme İlkesini (LSP) ihlal eder.


Bunun yerine şunu yapın:

 // ✅ 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> ); };


Revize edilen kodda, React'teki standart select öğesinin işlevselliğini genişletmeyi amaçlayan bir CustomSelect bileşenimiz var. Bileşen, value , iconClassName , handleChange gibi destekleri ve yayılma operatörünü ...props kullanan ek destekleri kabul eder. CustomSelect bileşeni, select öğesinin özelliklerinin kullanılmasına izin vererek ve ek destekleri kabul ederek, Liskov Değiştirme İlkesini (LSP) izler.


Arayüz Ayırma Prensibi

“Hiçbir kod kullanmadığı yöntemlere bağımlı olmaya zorlanmamalıdır.” — Vikipedi.


Arayüz Ayrıştırma Prensibi (ISP), arayüzlerin aşırı geniş olması ve müşterileri gereksiz işlevler uygulamaya zorlaması yerine, spesifik müşteri gereksinimlerine odaklanması ve uyarlanması gerektiğini önermektedir. Gerçek uygulamaya bakalım.


 // ❌ 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> ); }


Yukarıdaki örnekte, ürün detaylarının tamamını, gerektirmese de ProductThumbnailURL bileşenine aktarıyoruz. Bileşene gereksiz riskler ve karmaşıklık katar ve Arayüz Ayrıştırma İlkesini (ISP) ihlal eder.

ISP'ye bağlı kalmak için yeniden düzenleme yapalım:

 // ✅ 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> ); };


Revize edilen kodda ProductThumbnailURL bileşeni, ürün ayrıntılarının tamamı yerine yalnızca gerekli bilgileri alıyor. Gereksiz riskleri önler ve Arayüz Ayrıştırma Prensibini (ISP) destekler.

Bağımlılığı Ters Çevirme Prensibi

"Bir varlık somutlaştırmalara değil, soyutlamalara dayanmalıdır" - Vikipedi.


Bağımlılığı Ters Çevirme İlkesi (DIP), yüksek seviyeli bileşenlerin düşük seviyeli bileşenlere bağlı olmaması gerektiğini vurgular. Bu prensip gevşek bağlantıyı ve modülerliği teşvik eder ve yazılım sistemlerinin bakımını kolaylaştırır. Gerçek uygulamaya bakalım.


 // ❌ 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 bileşeni alt öğelerine sıkı bir şekilde bağlı olduğundan esnekliği engeller ve davranışını değiştirmeyi veya genişletmeyi zorlaştırır.

Bunun yerine şunu yapın:

 // ✅ 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> ); };


Revize edilen kodda formun soyutlaması görevi gören AbstractForm bileşenini tanıtıyoruz. onSubmit işlevini bir destek olarak alır ve form gönderimini yönetir. Bu yaklaşım, üst düzey bileşeni değiştirmeden form davranışını kolayca değiştirmemize veya genişletmemize olanak tanır.

Çözüm

SOLID ilkeleri, geliştiricilere iyi tasarlanmış, bakımı yapılabilir ve genişletilebilir yazılım çözümleri oluşturma yetkisi veren yönergeler sağlar. Geliştiriciler bu ilkelere bağlı kalarak modülerlik, kodun yeniden kullanılabilirliği, esneklik ve azaltılmış kod karmaşıklığı elde edebilirler.


Umarım bu blog değerli bilgiler sunmuştur ve bu ilkeleri mevcut veya takip eden React projelerinizde uygulamanız için size ilham vermiştir.


Meraklı kal; kodlamaya devam edin!


Referans:


Burada da yayınlandı.