تزریق وابستگیها یا DI چیست؟
تزریق وابستگیها یکی از انواع IoC بوده که در آن ایجاد و انقیاد (binding) یک وابستگی، در خارج از کلاسی که به آن نیاز دارد صورت میگیرد. روشهای متفاوتی برای ارائه این وابستگی وهله سازی شده در خارج از کلاس به آن وجود دارد که در ادامه مورد بررسی قرار خواهند گرفت.
یک مثال: بسته غذایی را که با خود به سر کار میبرید درنظر بگیرید. تمام اجزای مورد نیاز تشکیل دهنده یک بسته غذا عموما داخل آن قرار گرفته و حمل میشوند. حالت عکس آن زمانی است که در محل کار به شما غذا میدهند. این دقیقا همان حالتی است که تزریق وابستگیها کار میکند؛ یک سری سرویسهای خارجی، نیازهای کلاس جاری را برآورده میسازند.
در تصویر فوق یک طراحی مبتنی بر معکوس سازی کنترلها را مشاهده میکنید. وابستگیهای یک کلاس توسط اینترفیسی مشخص شده و سپس کلاسی وجود دارد که این وابستگی را پیاده سازی کرده است.
همچنین در این تصویر یک خط منقطع از کلاس MyClass به کلاس Dependency رسم شده است. این خط، مربوط به حالتی است که خود کلاس، مستقیما کار وهله سازی وابستگی مورد نیاز خود را انجام دهد؛ هر چند اینترفیسی نیز در این بین تعریف شده باشد. بنابراین اگر در بین کدهای این کلاس، چنین کدی مشاهده شد:
IDependency dependency= new Dependency();
در اینجا یک کلاس دیگر به نام Injector اضافه شده است که قابلیت تزریق وابستگیها را به کلاس نیازمند آن به روشهای مختلفی دارا است. کار کلاس Injector، وهله سازی MyClass و همچنین وابستگیهای آن میباشد؛ سپس وابستگی را دراختیار MyClass قرار میدهد.
تزریق وابستگیها در سازنده کلاس
یکی از انواع روشهای تزریق وابستگیها، Constructor Injection و یا تزریق وابستگیها در سازنده کلاس میباشد که متداولترین نوع آنها نیز بهشمار میرود. در این حالت، وابستگی پس از وهله سازی، از طریق پارامترهای سازنده یک کلاس، در اختیار آن قرار میگیرند.
یک مثال:
public class Shopper { private readonly ICreditCard _creditCard; public Shopper(ICreditCard creditCard) { _creditCard = creditCard; } }
ICreditCard creditCard = new MasterCard(); Shopper shopper = new Shopper(creditCard);
تزریق در خواص عمومی کلاس
Setter Injection و یا تزریق در خواص عمومی یک کلاس، یکی دیگر از روشهای تزریق وابستگیها است (setter در اینجا منظور همان get و set یک خاصیت میباشد).
در حالت تزریق وابستگیها در سازنده کلاس، امکان وهله سازی آن کلاس بدون ارسال وابستگیها به سازنده آن ممکن نیست؛ اما در اینجا خیر و امکان وهله سازی و استفاده از یک کلاس، پیش از اعمال وابستگیها نیز وجود دارد. بنابراین امکان تغییر و تعویض وابستگیها پس از وهله سازی کلاس نیز میسر است.
public class Shopper { public ICreditCard CreditCard { get; set; } } ICreditCard creditCard = new MasterCard(); Shopper shopper = new Shopper(); shopper.CreditCard = creditCard;
تزریق اینترفیسها
تزریق اینترفیسها نیز یکی دیگر از روشهای تزریق وابستگیها است؛ اما آنچنان استفاده گستردهای برخلاف دو روش قبل نیافته است.
در این روش نه از سازنده کلاس استفاده میشود و نه از یک خاصیت عمومی. ابتدا یک اینترفیس که بیانگر ساختار کلاسی که قرار است تزریق شود ایجاد میگردد. سپس این اینترفیس را در کلاس وابستگی مورد نظر پیاده سازی خواهیم کرد. در این اینترفیس نیاز است متد خاصی تعریف شود تا کار تزریق وابستگی را انجام دهد.
یک مثال:
public interface IDependOnCreditCard { void Inject(ICreditCard creditCard); } public class Shopper : IDependOnCreditCard { private ICreditCard creditCard; public void Inject(ICreditCard creditCard) { this.creditCard = creditCard; } } ICreditCard creditCard = new MasterCard(); Shopper shopper = new Shopper(); ((IDependOnCreditCard)shopper).Inject(creditCard);
در ادامه کلاس خریدار، اینترفیس IDependOnCreditCard را پیاده سازی کرده است. به این ترتیب کلاس Injector تنها کافی است بداند تا این متد خاص را باید جهت تنظیم و تزریق وابستگیها فراخوانی نماید. این روش نسبتا شبیه به روش Setter injection است.
این روش بیشتر میتواند جهت فریم ورکهایی که قابلیت یافتن کلیه کلاسهای مشتق شده از IDependOnCreditCard را داشته و سپس میدانند که باید متد Inject آنها را فراخوانی کنند، مناسب است.
نکاتی که باید حین کار با تزریق وابستگیها درنظر داشت
الف) حین استفاده از تزریق وابستگیها، وهله سازی به تاخیر افتاده وابستگیها میسر نیست. برای مثال زمانیکه یک وابستگی قرار است در سازنده کلاسی تزریق شود، وهله سازی آن باید پیش از نیاز واقعی به آن صورت گیرد. البته امکان استفاده از اشیاء Lazy دات نت 4 برای مدیریت این مساله وجود دارد اما در حالت کلی، دیگر همانند قبل و روشهای متداول، وهله سازی تنها زمانیکه نیاز به آن وابستگی خاص باشد، میسر نیست. به همین جهت باید به تعداد وابستگیهایی که قرار است در یک کلاس استفاده شوند نیز جهت کاهش مصرف حافظه دقت داشت.
ب) یکی از مزایای دیگر تزریق وابستگیها، سادهتر شدن نوشتن آزمونهای واحد است. زیرا تهیه Mocks در این حالت کار با اینترفیسها بسیار سادهتر است. اما باید دقت داشت، کلاسی که در سازنده آن حداقل 10 اینترفیس را به عنوان وابستگی دریافت میکند، احتمالا دچار مشکلاتی در طراحی و همچنین مصرف حافظه میباشد.