Di در برنامه نویسی - بخش اول

سلام خدمت همه دوستای عزیزم ، من یعنی محسن نادرزاده ، اومدیم با یه مقاله دیگه ، مدتها بود که به خاطر درگیریهای دانشگاهم نمیتونستم روی یادگیری مهارتهای جدید کار کنم و کمی از تکنولوژیهای جدید اندروید دور بودم ، ولی به تازگی با چک لیستی که من تهیه کردم ، شروع کردم به یادگیری اونها که یکیش di بود . اولین جایی که به عنوان سورس یادگیری رفتیم سراغش ، وب سایت دوست و یار همیشگی برنامه نویسا یوتیوب عزیز بود . به پیشنهاد یکی از عزیزان رفتیم سراغ چنل CodingInflow که احتمالا برای خیلی از شما همکاران آشناست . در یک سری مجموعه مقالات قرار هستش که چیزایی که من از آموزشهای این چنل در مورد DI یاد گرفتم رو براتون منتشر کنم ، بیشتر از این منتظرتون نمیزارم ، بریم سراغ بخش اول مقاله :

اول از همه بیایم باهم ببینیم اصلا Dependency توی کلمه Dependency Injection معنیش چیه ؟

معنی dependency : منظور از dependency آبجکتهایی هستش از کلاس های مختلف ، که یک کلاس میتونه برای انجام کار های خودش ، به اونها نیاز داشته باشه. مثلا در نظر بگیرید که ما دو کلاس به نامهای A و B داریم . کلاس A برای انجام وظیفه های خودش به نمونه یا instance ای از کلاس B نیاز داره. در چنین حالتی گفته می شود که کلاس B ، جزو Dependency های کلاس A هستش.

حالا بریم سراغ کلمه دوم یعنی Injection :

وقتی از این کلمه توی برنامه نویسی برای مفهوم DI استفاده می کنیم منظورمون این هستش که Dependency های یک کلاس، باید خارج از کلاس تهیه و ساخته بشن و بعد به کلاس داده بشن تا کلاس بتونه کارشو انجام بده و در حقیقت خود کلاس نباید مسئولیتی برای فراهم کردن Dependency های خودش داشته باشه.

توی تکه کد بالا ، ما یک کلاس Car داریم که مفهوم اتومبیل در دنیای واقعی رو نشون میده . یک اتومبیل برای اینکه بهش اتومبیل یا وسیله نقلیه اطلاق بشه ، دو مفهوم اساسی رو نیاز داره ، یکی موتور اتومبیل و دیگری چرخهای اون هستش . در مثال بالا ، کلاس Car ما دو پارامتر رو که کلاسهای موتور یا Engine و کلاس چرخ ها یا Wheels باشن رو به عنوان پارامتر ورودی متد سازنده دریافت می کنه، یعنی ما برای ساختن یک instance از این کلاس باید دو تا instance از کلاسهای Engine و Wheels رو هم فراهم کنیم. در زمانی که ما چنین سناریویی داریم چند روش داریم تا این Dependency ها را برای کلاس فراهم کنیم :

1- ساختن آبجکتهای مورد نیاز داخل خود کلاس : توی چنین حالتی ، ما وقتی داریم آبجکتی از Dependency کلاس (مثلا کلاس Engine) رو داخل کلاس تعریف می کنیم ، توی همون لاین هم میایم اون رو مقداردهی یا اصطلاحا initialize می کنیم . اما این کار اشتباهه ، شاید کدمون کار کنه ولی این کار اصولی نیست، چرا که بالاتر گفتیم کلاس نباید خودش مسئولیتی برای فراهم کردن Dependency های خودش داشته باشه و با اینکار اصولا خود کلاس ما خودش مسئول ساختن dependency هاش هستش که این خب این اشتباهه.

2- دریافت dependency ها از طریق متد سازنده

3- دریافت dependency ها از طریق متدهای Setter

اگه بخوایم حالت شماره یک رو در دنیای واقعی در نظر بگیریم ، باید مثال خط مونتاژ اتومبیلی رو در نظر بگیریم که کارکنان این خط وقتی نوبت به نصب چرخها و موتور میرسه ، باید از صفر یعنی پیچ ها و تهیه مواد اولیه شروع به ساختن موتور و چرخها کنن و اینکار رو برای هر اتومبیل داخل خط انجام بدن در حالی که این کار اصولی نیست و می بایست موتور و چرخها ، به صورت موازی با بدنه اتومبیل در خط تولید جداگانه ای تولید بشن و کارکنان خط مونتاژ فقط مسئولیت نصب اونها روی بدنه اتومبیل رو داشته باشن. توی چنین حالتی اگه ساختار موتور تغییری بکنه کار کارکنان خط مونتاژ با مشکل مواجه نمیشه.

فراهم کردن Dependency های کلاس از روش شماره دو یا روش شماره سه چند مزیت داره : اول مسئولیت ساخت dependency ها از خود کلاس گرفته میشه و اصطلاحا ما کلاس رو از ساختن dependency های خودش میایم و Decouple می کنیم. از مزایای Decouple شدن این موضوع ها از هم دیگه، میشه به Unit test اشاره کرد چرا که توی Unit test ، هدف تست یک کامپوننت ایزوله جدای dependency های اون کامپوننت هستش.

توی پروژه های بزرگ موارد زیادی وجود داره که Dependency های کلاس ما ، جزو Dependency های پر هزینه هستش مثل Database ، وب سرویس و... . و ما هنگام Unit test نیازی نداریم تا این Dependency ها رو تست کنیم.معمولا توی unit test به جای استفاده از آبجکتهای واقعی این Dependency های پر هزینه ، این Dependency ها رو اصطلاحا mock یا شبیه سازی می کنیم و فقط از آبجکتهایی استفاده می کنیم که صرفا رفتار اونها رو میاد و برای ما شبیه سازی میکنه. یک سری سناریو هم توی برنامه نویسی ممکنه پیش بیاد که ما مجبور هستیم تا از یک آبجکت مشترک برای تامین dependency های چندین کلاس استفاده کنیم. مثال خط مونتاژ اتومبیل رو توی ذهنتون دوباره تجسم کنید، فرض کنید که بعد از اتمام مونتاژ هر اتومبیل ، راننده ای باید اتومبیل ساخته شده رو به بیرون خط مونتاژ ، هدایت کنه ، توی چنین حالتی لازم نیست تا برای هر اتومبیل جدید، یک راننده جداگانه داشته باشیم و ما می تونیم از یک راننده برای تمام اتومبیلها استفاده کنیم. توی برنامه نویسی هم این سناریو گاهی پیش میاد، مثلا شما از یک instance از کلاس HttpClient ممکنه برای انجام چند کار مختلف و متفاوت مرتبط با شبکه استفاده کنید برای مثال استفاده از یک http client برای دانلود و اپلود و همچنین ارسال درخواستهای http به سرور. توی حالتی که ما dependency های خودمون رو از طریق متد سازنده دریافت می کنیم انجام چنین کاری غیرممکنه اما اگه از di استفاده کنیم اینکار قابل انجامه.

پس تا اینجای مطالب میشه نتیجه گرفت که DI یک دیزاین پترن محسوب میشه نه یک لایبری . توی هر اپلیکیشن هم به نوعی از DI استفاده میشه . ما اگه بخوایم توی حوزه برنامه نویسی اندروید چند مثال بزنیم ، زمانی که ما برای یک ریسایکلر ویو با استفاده از متد setAdapter ، اداپتر ست می کنیم در حال انجام DI هستیم، حتی زمانی که یک مقدار متنی یا String رو به متد سازنده یک کلاس پاس میدیم هم باز در حال انجام DI هستیم بنابراین ، برای اینکه DI را در نرم افزار و پروژه خود را داشته باشیم ، الزامی برای استفاده از لایبری نداریم.

توی بخش بعدی مقاله ، ما به معرفی لایبری Dagger ، و اینکه اصلا این لایبری چی هستش و چرا ما باید از این لایبری یا لایبریهای مشابه استفاده کنیم ، اشاره می کنیم. بچه ها محتوای این مقاله با زحمتهای خانم الهه آرین عزیز و من تهیه شده و میشه گفت در حقیقت نسخه ترجمه شده ای از آموزشهای کانال یوتیوب CodingInflow هستش . اگر جایی در مقالات ما اشتباهی دیدید ، خوشحال میشیم باهامون درمیون بزارید ، ما مشتاق یادگیری هستیم و خوشحال میشیم شما عزیزان و اساتید پیشنهادات و انتقاداتتون رو برای ما بنویسید . در آخر باید تشکر کنم ازتون که تا اینجای مقاله رو خوندید :))))) دمتون گرم دوستتون داریم .