Dependency Injection | تزریق وابستگی
توضیحات جلسه
جزوه و مستندات
مفاهیم کلیدی
- Dependency Injection (DI)
الگویی که در آن یک object، وابستگیهای موردنیاز خود را از بیرون دریافت میکند، نه اینکه آنها را داخل class خودش بسازد. این کار tight coupling را کاهش میدهد و کد را قابلتستتر و قابلاستفادهمجددتر میکند.
- Dependency
objectای که یک class برای انجام کار خود به آن نیاز دارد؛ مثل Engine برای Car.
- Tight Coupling
وابستگی شدید بین classها؛ بهطوریکه تغییر در یک implementation باعث تغییر در چندین class دیگر میشود.
- Loose Coupling
طراحیای که در آن classها تا حد ممکن به جزئیات پیادهسازی وابسته نیستند و تغییر یک بخش، اثر کمی روی بخشهای دیگر دارد.
- Inversion of Control (IoC)
اصلی که در آن مسئولیت ساخت و مدیریت objectها از کد برنامه به framework یا container منتقل میشود.
- Constructor Injection
رایجترین نوع DI که در آن وابستگیها از طریق constructor به class داده میشوند.
- Interface-based Design
طراحیای که بهجای وابستگی مستقیم به یک class مشخص، به interface وابسته میشود تا امکان جایگزینی implementationها فراهم شود.
EngineInterface
یک قرارداد مشترک برای همه موتورها که امکان استفاده از چندین implementation مختلف را فراهم میکند.
LegacyEngine
یک implementation قدیمی از Engine که نمونهای از وابستگی مستقیم و قدیمی را نشان میدهد.
NewEngine
یک implementation جدید از Engine که بدون تغییر در Car قابل جایگزینی است.
- Reusability
قابلیت استفاده مجدد از یک وابستگی در چند class مثل Car، Bike و Scooter بدون تکرار کد.
- Testability
وقتی وابستگی از بیرون تزریق میشود، در تست میتوان بهراحتی mock یا stub جایگزین کرد.
- Maintainability
بهدلیل کاهش وابستگی مستقیم، تغییر implementationها سادهتر و کمهزینهتر میشود.
موارد مصاحبه ای
- Dependency Injection چیست؟
الگویی است که در آن object وابستگیهای خود را از بیرون دریافت میکند، نه اینکه خودش آنها را بسازد.
- DI چه مشکلی را حل میکند؟
مشکل tight coupling را کاهش میدهد و طراحی را انعطافپذیرتر، قابلتستتر و قابلنگهداریتر میکند.
- تفاوت
DIوIoCچیست؟
DI یک روش برای تزریق وابستگی است، اما IoC اصل کلی انتقال کنترل ساخت و مدیریت objectها به container یا framework است.
- چرا وابستگی مستقیم مثل
new LegacyEngine()مشکلساز است؟
چون Car به یک implementation خاص وابسته میشود و تغییر موتور، نیاز به تغییر مستقیم در Car و سایر classها دارد.
- چرا استفاده از
interfaceدر DI مهم است؟
چون class به قرارداد وابسته میشود نه به جزئیات پیادهسازی، و این باعث جایگزینی آسان implementationها میشود.
- Constructor Injection چه مزیتی دارد؟
وابستگی را اجباری و شفاف میکند و class را از ابتدا در حالت معتبر میسازد.
Springچگونه DI را انجام میدهد؟
IoC Container beanها را میسازد، وابستگیها را شناسایی میکند و آنها را به classهای موردنیاز تزریق میکند.
- چرا DI تستپذیری را بهتر میکند؟
چون در تست میتوان بهجای وابستگی واقعی، نسخههای mock را تزریق کرد.
- چرا DI نگهداری کد را بهتر میکند؟
چون تغییر در یک وابستگی باعث تغییر گسترده در classهای مصرفکننده نمیشود.
- چرا
Carنباید خودشEngineرا بسازد؟
چون ساخت وابستگی باید از منطق استفاده جدا باشد تا طراحی loosely coupled باقی بماند.
- مزیت استفاده مجدد از یک dependency در چند
classچیست؟
کاهش تکرار، کاهش هزینه تغییر و یکپارچگی بیشتر در رفتار سیستم.
سناریو کاربردی
- سناریوی خودرو و موتور
در مثال، Car برای کار کردن به Engine نیاز دارد. اگر Car خودش LegacyEngine را بسازد، به آن پیادهسازی خاص وابسته میشود. با DI، Engine از بیرون به Car تزریق میشود.
- روند حل مسئله
ابتدا یک interface به نام Engine تعریف میشود تا قرارداد مشترک ایجاد شود. سپس چند implementation مثل LegacyEngine و NewEngine ساخته میشوند. بعد Car از طریق constructor وابستگی را دریافت میکند. در نتیجه، میتوان بدون تغییر در Car، موتور متفاوتی تزریق کرد.
- استفاده با
Spring
در Spring، یک کلاس تنظیمات با @Configuration و یک متد @Bean برای ساخت Engine تعریف میشود. سپس Car با @Component و @Autowired وابستگی خود را دریافت میکند. در زمان اجرا، ApplicationContext Car را از container برمیدارد و متد drive() را اجرا میکند.
- خروجی مفهومی
New Engine Started
Car is Running
این نشان میدهد که Car بدون وابستگی مستقیم به پیادهسازی خاص موتور، با Engine تزریقشده کار میکند.
بیشتر بدانید
- نکته طراحی
بهجای وابستگی به classهای concrete، تا حد امکان به interface وابسته شوید.
- نکته معماری
DI در پروژههای enterprise Java و مخصوصاً با Spring یک الگوی بسیار رایج است.
- نکته تست
اگر وابستگیها از بیرون تزریق شوند، در unit test بهراحتی میتوان mock object جایگزین کرد.
- نکته توسعه
یک dependency مثل Engine میتواند در چند class مانند Car، Bike و Scooter استفاده شود، بدون اینکه منطق آنها به پیادهسازی خاصی وابسته باشد.
- نکته
Spring
IoC Container فقط object نمیسازد؛ بلکه چرخه عمر beanها را هم مدیریت میکند.
- نکته فایل
pom.xml
برای استفاده از قابلیتهای Spring MVC باید dependency مناسب را در pom.xml اضافه کرد.
