چند سالی هست که مهندس نرم افزار هستم، اما هر از چندگاهی دوست دارم به سمت اصول اولیهام برگردم. همه ما میدانیم که باید از اصول SOLID پیروی کنیم، اما دانش معمول همیشه یک روش معمول نیست. اخیراً متوجه شدم که واقعاً تفاوت بین وارونگی وابستگی (Dependency Inversion) و تزریق وابستگی (Dependency Injection) را نمیدانم. من مفهوم تقریبی آنها را درک میکردم، اما مطمئن نبودم که تفاوت بین این دو چیست. و علاوه بر این، نمیدانستم که چرا به اولی، آن وارونگی وابستگی میگویند. دقیقاً چه چیزی وارونه میشود؟
به نظر میرسد که مانند همه چیزهای خوب، داستانی پشت این قضیه وجود دارد. اما پیش از هر چیز، اجازه دهید تفاوت بین این دو مفهوم را مورد بحث قرار دهیم.

وارونگی وابستگی (Dependency Inversion)
وارونگی وابستگی یک اصل طراحی است. به عبارت سادهتر، به این معناست که ما نباید به پیادهسازیهای سطح پایین وابسته باشیم، بلکه باید به انتزاعات سطح بالا تکیه کنیم. و این منطقی است. زیرا به ما این امکان را میدهد که نسبت به جزئیات پیادهسازی ناشناس عمل کنیم. چه بخواهیم یک پیادهسازی خاص را تغییر دهیم و چه بخوایم از پیادهسازیهای دیگر پشتیبانی کنیم، کد دست نخورده باقی میماند. زیرا از جزئیات پیادهسازی سطح پایین جدا شده است.
برای تفهیم این مفهوم، بیایید فرض کنیم که شما یک مهندس نرم افزار هستید. شما دو وابستگی دارید. ادیتور کد و غذایتان. شما نمیخواهید ساعت به ساعت زندگیتان را درگیر VSCODE باشید. و همچنین نمیخواهید تمام روز را پیتزا بخورید. همچنین، اگر جزئیات پیادهسازی پیتزا را تغییر دهند، و یک پیتزای بدمزه نصیب شما شود، چه اتفاقی میافتد؟
راه حل این مشکل وحشتناک، وابسته به انتزاعات سطح بالاست. شما به مقداری ویرایشگر نیاز دارید، و به مقداری غذا هم نیاز دارید، اما ترجیح میدهید دست خود را در انتخاب گزینههایتان باز نگه دارید. اگر پیتزا شروع به خراب شدن کرد، همیشه میتوانید از پیادهسازی غذاهای دیگر استفاده کنید. مثل غذای ژاپنی و حتی همبرگر.
تزریق وابستگی
تزریق وابستگی یک الگوی طراحی است که به ما امکان میدهد ساختن را از استفاده جداسازی کنیم. این الگو به ما اجازه میدهد تا آبجکتهای مورد نیاز را در run-time تزریق کنیم، بدون اینکه نگران ساختن آنها باشیم. این الگو همچنین تمایل دارد دست در دست با اصل وارونگی وابستگی کار کند.
به عنوان مثال، ما میتوانیم به کلاسهای انتزاعی سطح بالا وابسته باشیم و یک پیادهسازی خاص را در زمان اجرا، بر اساس کاربرد مورد نیاز، تزریق کنیم. این به ما امکان میدهد کدهای قابل تنظیم و پویاتری بنویسیم. حتی توانایی ما را برای آزمایش کدمان بهبود میبخشد. زیرا به ما امکان میدهد به راحتی کلاسهای تزریق شده را نادیده بگیریم. بنابراین میتوانیم بر روی تستینگ منطق هستهای (core logic) خود تمرکز کنیم.
مفهوم تزریق وابستگی
برای تفهیم این مفهوم، بیایید فرض کنیم که باز هم شما یک مهندس نرم افزار هستید. با همان وابستگیها: ادیتور کد و غذا.
مطمئناً نه مایل به این هستید که خودتان پیتزا درست کنید و نه میخواهید خودتان ادیتور کد را بسازید. شما یک مهندس نرم افزار هستید که سرتان شلوغ است و برای این کارها وقت ندارید.
خوشبختانه، شما تزریق وابستگی را در کنارتان دارید!
شما نیازی به تهیه پیتزا یا ایجاد ادیتور کد ندارید. میتوانید فرض کنید که از قبل برایتان ایجاد شده است و دقیقاً در جایی که به آن نیاز دارید، تزریق شده است.
همچنین، شما کل روزتان را درگیر پیتزا و VSCODE نخواهید بود. شما میتوانید غذا را آگنوستیک کنید و اجازه دهید پیکربندی تصمیم بگیرد که آیا امروز میخواهید پیتزا بخورید (یا به شما تزریق شود! ای وای!) یا خیر.
بنابراین، اساساً تزریق وابستگی ما را قادر میسازد تا از وارونگی وابستگی استفاده کنیم و بر انتزاعات سطح بالا تکیه کنیم.
با این حال، من هنوز توضیح ندادهام که چرا به آن «وارونگی» میگویند. دقیقاً چه چیزی در اینجا وارونه شده است؟ خب. من به شما قول یک داستان را داده بودم! (نگران نباش! کوتاه است.)
چرا ما آن را وابستگی وارونگی مینامیم؟
خب. به نظر میرسد که در توسعه سنتی نرم افزار، بهترین روش ساختن نرم افزار معماری لایهای بود که امکان ساخت سیستمهای پیچیدهتر را فراهم کرد.
در این معماری، اجزای سطح بالاتر برای دستیابی به یک کار پیچیده، مستقیماً به اجزای سطح پایینتر وابسته اند.
پیتزاپزی – روش قدیمی در مقابل روش «وارونه»
برای نشان دادن وارونگی، به تابع preparePizza
در خطوط زیر نگاه کنید:
function preparePizza() {
const grandmasPizzaRecipe = GrandmasPizzaRecipe()
const grandmaPizzaPreparer = GrandmaPizzaPreparer()
return grandmaPizzaPreparer.preparePizza(grandmasPizzaRecipe)
}
تابع سطح بالا، برای تکمیل وظیفه خود به دو پیادهسازی خاص متکی است.
حالا بیایید به مثالی با استفاده از تزریق وابستگی و وارونگی وابستگی نگاهی بیندازیم:
function preparePizza(recipe: IPizzaRecipe, preparer: IPizzaPreparer) {
return preparer.preparePizza(recipe)
}
در این مثال، ما برای تکمیل وظیفه خود به انتزاعات سطح بالا تکیه میکنیم. بنابراین میتوانیم پیتزا را با دستور پخت آنلاین بپزیم تا مادربزرگ هم استراحتی بکند.
آنچه امروز آموختیم
ما به تفاوت میان تزریق وابستگی و وارونگی وابستگی و نحوه کار آنها برای تولید یک نرم افزار عالی پرداختیم. ما همچنین در مورد اینکه چرا از اصطلاح «وارونگی» استفاده میکنیم، بحث کردیم و در طول مسیر، با تاریخچه مختصر نرم افزار آشنا شدیم.
امیدوارم این مقاله برای شما مفید بوده باشد!
منبع: betterprogramming نویسنده: گای اِرِز، مهندس نرم افزار و مشتاق به یادگیری!
Leave feedback about this