Dependency Injection (DI)

تزریق وابستگی یا dependency injection یک روش طراحی(design pattern) در مهندسی نرم‌افزار است تا کدهایی بهتر و منعطف‌تر تولید کنیم.اما چگونه؟ این روش در عین سادگی، سوء تفاهم‌هایی ایجاد می‌کند که در آخر به آن اشاره خواهم کرد.اما این مطلب به زبان خیلی خیلی خیلی ساده چه می‌خواهد بگوید؟فرض کنید که سه برنامه نویس در سه گوشه‌ی دنیا، هر یک در حال تولید نرم‌افزار هستند..برنامه‌نویس اول: آمریکا،برنامه‌نویس دوم: ژاپن، و برنامه‌نویس سوم در استرالیا است.این سه نفر هیچگاه همدیگر را ندیده‌اند و نخواهند دید.برنامه نویس اول روزی با خود فکر می‌کند و برنامه‌ای به ذهنش می‌آید که به صورت زیر آن را در زبان جاوا پیاده‌سازی می‌کند. این برنامه تنها یک کلاس دارد. این کلاس شامل یک تابع و یک constructor است:برنامه‌نویس اول از کد خود به وجد امده و خود را استالمنِ زمانه می‌بیند!!!این برنامه‌نویس، کد خیلی خفن خودش رو کامپایل می‌کند و توی اینترنت به شکل یک کتابخانه به نام A.jar آپلود می‌کند.برنامه‌نویس دوم که داشته چرخ میزده توی اینترنت یهو این برنامه رو میبینه و خوشش می‌آید. در ادامه یه ایده به ذهنش می‌رسه که می‌خواد از برنامه‌ی قبلی توی برنامه‌ی خودش استفاده کنه (طبیعتا با رعایت حقوق مولف).پس بعد از اجازه گرفتن از برنامه‌نویس اول، کد زیر رو می‌نویسه:او نیز حس می‌کند توروالدزِ درونش دارد شکوفا می‌شود.این هم نتیجه‌ی زحمات برنامه‌نویس دوم. از برنامه‌ی قبل در کدهایش استفاده کرده و میبینید که برنامه‌ش نسبت به برنامه‌ی قبلی کارهای بیشتری می‌تواند بکند. پس خوشحال و شادان کدهایش را کامپایل می‌کند و یک فایل B.jar می‌سازد و در اینترنت پخش می‌کند. اما در این حین متوجه نکته‌ای هم می‌شود: اینکه حجم فایلش به طرز غیرمعمولی سنگین‌تر شده است! چرا؟ چون کدهای نفر قبلی را هم، درون کدهای خودش چپانده و حالا کدها به شکلِ تجمعی دارند زیاد می‌شوند.. همینطور پیش برود و چند نفر دیگر اگر ازین کدها استفاده کنند دیگر آن فایل را باید با تریلی حمل کرد..حالا چه می‌شود؟ خب نوبت برنامه‌نویس سوم است. وی تنها قصد دارد از برنامه‌ی نفر دوم استفاده کرده و خروجی آن را صرفا ببیند. (او نمی‌خواهد کدی به کدهای قبلی اضافه کند و کتابخانه‌ی جدیدتری تولید کند):این روالی است که ادم فکر می‌کند در پروژه‌های بزرگ اتفاق می‌‌افتد.. ولی در واقع اینگونه نیست. دلایلش را مفصل در ادامه خواهم آورد. فعلا شیوه‌ی درست را ببینید:‌‌‌‌استفاده از Dependency Injection Design Patternحالا فرض کنید که برنامه‌نویسان ما می‌خواهند به شیوه‌ی اصولی‌تری کار کنند.برنامه‌نویس اول که نیازی نیست تغییری در کدهایش بدهد.ولی برنامه‌نویس دوم باید به این گونه عمل بکند:استفاده از DI بسیار ساده است!و این هم برای برنامه‌نویس سوم(که در حقیقت همان استفاده کننده‌ی نهایی است):در یک جمله، DI یعنی چه؟به جای اینکه وابستگی‌ها(dependency) را درون کلاس‌ها بسازیم، درون تابع main می‌سازیمشان. آنگاه به وسیله تابع سازنده آن‌ها را به کلاس مربوطه پاس دهیم(تزریق می‌کنیم).حدس می‌زنم که تا به اینجا متوجه اهمیت ‌‌DI شده باشید، اما به به عنوان جمع‌بندی نهایی، مطالب زیر را بخوانید تا تسلط‌تان به موضوع کامل شود.۱- اگر نخواهیم به شیوه‌ی DI کد بنویسیم باید کتابخانه‌هایی با حجم بالا (که طبیعتا دیگر غیرقابل دیباگ کردن شده‌اند) را تحمل کنیم.۲- بدون DI، قابلیت ماژولاریتی از بین رفته و دیگر کدها قابلیت ارتقا نخواهند داشت. مثلا فرض کنید که شخص چهارمی پیدا بشود و اعلام کند که برنامه‌ای نوشته که دقیقا کار برنامه‌ی A را انجام می‌دهد، ضمن اینکه مصرف حافظه‌ی آن را به نصف تقلیل داده است. آيا این کد قابلیت استفاده در برنامه‌ی دوم را به راحتی دارد؟ مسلما خیر.۳- عمل ایجاد اشیا به جای زمان کامپایل، در زمان اجرا انجام می‌شوند. نتیجه اینکه این اصل، فقط در زبان‌های کامپایلری معنی دارد و زبان‌های مفسری به‌ آن احتیاجی نخواهند داشت.۴- تست‌پذیری در این روش بسیار بالا می‌رود. (توضیح در اینجا و اینجا)۵- مصرف حافظه محدودتر میشود.خب معایب این روش چیست؟کد کمی ناخواناتر و پیچیده‌تر به نظر می‌رسد. (بار دیگر کدهای نفر سوم را با هم مقایسه کنید).سوء تفاهمی که در بالا به آن اشاره کرده بودم در واقع این‌ست که افراد وقتی برای اولین بار این اصل را درک می کنند می‌گویند بابا آخه لازم بود این همه صغرا کبرا بچینی و یه اسم به این بزرگی براش بزاری در حالی که خودش در واقعیت دو تا خط کد بیشتر نیست؟جواب: خب بله. درسته که ساده‌س ولی خیلی مهمه و برای از قلم نیوفتادن‌ش هم هست که اینقدر براش آب و تاب به خرج می‌دن.باور نمی‌کنید؟ به عنوان منبع، اینجا(James Shore) و اینجا(Martin Fowler) رو ببینید. این‌ها آدم حسابی‌های دنیای کامپیوترند..خب امیدوارم که مطالب مفید بوده باشد برایتان! با آرزوی سلامتی!