تزریق وابستگی و DDD
بسم الله الرحمن الرحیم
آموزش تزریق وابستگی
با توجه به اینکه چندین مطلب در مورد DI قرار دادم اما با توجه به مطلبی با عنوان معرفی متدولوژی DDD که برای دوستان قرار دادم و مثالی را در آن مطرح کردم و تصمیم گرفتم چند نکته جدید در بحث DI را در ادامه مثال مطلب DDD خدمتتان عرض کنم
همه با DI آشنا هستیم (در صورت عدم آشنایی لطفا مطالب قبلی را مطالعه نمایید) در مثال مطرح شده در مطلب DDD کدی به صورت زیر داشتید (پیشنهاد می کنم مطلب مربوطه را با عنوان معرفی متدولوژی Domain Driven Design برای درک هر چه بهتر این مطلب مطالعه فرمایید هر چند در صورت عدم مطالعه آن بحث زیر را متوجه خواهید شد) :
public class Account
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
public class People
{
public Account account { get; set; }
public int Id { get; set; }
public string FullName { get; set; }
public string NationalCode { get; set; }
}
public class Report
{
public People people { get; set; }
public int Id { get; set; }
public string Text { get; set; }
public DateTime RegisterDate{ get; set; }
}
همانطور که مشخص است ما سه کلاس داریم و کلاس گزارش از کلاس افراد(People) استفاده می کند خوب مسئله همین است ما می خواهیم تزریق وابستگی را به صورت بهینه انجام دهیم.
در تزریق وابستگی می بایست پیچدگی را با Interface حل کنیم . پس کد ما به صورت زیر شود:
public class Report
{
IPeople people= new People();
public int Id { get; set; }
public string Text { get; set; }
public DateTime RegisterDate{ get; set; }
}
در اینجا ما وابستگی مربوطه را به وسیله یک interface تا حدی برطرف کردیم اما مشکل هنوز پابرجاست چرا که ما مستقیما از کلاس People هنوز داریم استفاده می کنیم و هدف ما حذف این رابطه بود. راه حلی که اینجا مطرح می شود DI است که با نام Inversion of control (واگذاری مسئولیت) یا به اختصار IOC شناخته می شود.
حال این مشکل را به شکل زیر حل می کنیم:
public class Report
{
Private IPeople people;
Public Report(IPeople _people)
{
people=_people;
}
}
اکنون دیگر کلاس Report به People وابسته نیست، بلکه به شیئ وابسته است که Interface،People را پیاده سازی کرده است اما کدام شیئ؟
مشخص نیست تا زمانی که شیئ از کلاس گزارش را ایجاد نکرده باشیم و نمونه ای از کلاسی که اینترفیس People را پیاده سازی کرده است برای آن، مشخص نکنیم، هیچ ذهنیتی در مورد آن ندارد.
به این نوع وابستگی که از طریق سازنده تزریق می شود Constructor Injection یا به اصطلاح تزریق از طریق سازنده می گویند اما روش دیگری نیز وجود دارد که به آن Setter Injection می گویند. در روشی که از طریق سازنده ایجاد می شود به اجبار در زمان ساخت یک شی از کلاس تزریق انجام می شود اما در روش دوم به این صورت نیست. در طراحی به صورت Strategy Pattern از روش دوم باید استفاده کرد.
در اینجا مثالی از روش دوم نیز قراردادم من شخصا روش دوم را می پسندم چون چندین مزیت دارد که برخی از آنها که شخصا در پروژه مشاهده کردم بدین صورت است:
- نیازی نیست تزریق را حتما انجام دهید (البته در صورتی که فقط یک تابع سازنده داشته باشید!)
- لازم نیست حتما در زمان ساخت نمونه تزریق صورت گیرد
- برعکس روش اول که وقتی نمونه ای ایجاد شد دیگر نمی توان وابستگی ها را تغییر داد در این روش می توان
- توسعه دهنده نحوه تزریق و زمان آن را می تواند تعیین کند
- آگاهی از نحوه عملکرد را افزایش می دهد
- و..
مثالی از روش Setter Injection
public class Report
{
Public IPeople people{get; set;}
{
...
اگر بخواهیم از طریق سازنده تزریق را انجام دهیم مشکل هنوز وجود دارد چرا که باید به ازای هر نمونه سازی، نمونه ای از کلاس را انتقال دهیم و این روش خوبی نیست مثلا کد زیر را در نظر بگیرید، فکر کنید هر کدام از این کلاس ها برای نمونه سازی در تابع سازنده خود نیاز به یک نمونه از کلاس دارند تا تزریق وابستگی انجام گردد
دقیقا مثل فراخوانی Report اما با کدی طولانی تر
MyClass obj= new MyClass(new MyClass1(new MyClass2(new MyClass3)));
در واقع در این کد هر کلاس برای ساخته شدن نیاز به یک شی از کلاس دیگر دارد.
برای حل این مشکل روشی وجود دارد.
DI Container
در واقع وظیفه DI Container فقط تزریق وابستگی است و تزریق نیز فقط در یک مکان صورت می گیرد.
همانطور که دوستان واقف هستند ما در مجموعه خود از Ninject استفاده می کنیم که آن را برای سایر دوستان در بخش دیگری معرفی کرده ام