Dependency Injection :

من ارسلان میربزرگی، قصد دارم تا در مورد این موضوع، در این مقاله با شما صحبت کنم. تزریق وابستگی یا dependency injection، نوعی الگوی طراحی است که هدف آن، حذف وابستگی بین دو کلاس از طریق ایجاد کد یا کدهای مختلف  است. اگر نگاهی به وسایلی که روزانه با آنها سروکار دارید بیندازید، خواهید دید که این وسایل متشکل از قطعات مختلفی هستند. وسایلی مانند کامپیوتر یا لپ تاپ که از صفحه نمایش، رم، پردازنده، فن، پاور و ….تشکیل شده اند. هر یک از این قطعات، در صورتی که به هر علت دچار آسیب شوند، معمولا اولین کاری که می‌کنید این است که نسبت به تعویض این قطعات اقدام می‌کنید اما فرض کنید این قطعات قابل تعمیر نباشند. در این صورت، به محض آسیب دیدن قطعه مورد نظر، باید کلا وسیله خودتان را تعویض کنید و یک نمونه نو از آن را خریداری کنید.
برای اینکه این مشکل ایجاد نشود، تولید کنندگان لوازم الکترونیکی، معمولا قطعات هر وسیله را قابل تعمیر یا تعویض می‌سازند که به آن طراحی ماژولار گفته می‌شود. در حوزه برنامه نویسی نیز همین وضعیت میتواند حاکم باشد. به این معنی که برنامه نویس، وابستگی‌های موجود در برنامه را از سطح کلاس کاهش می‌دهد و در زمانی که نیاز باشد، مجددا آن را اضافه می‌کند. به این کار تزریق وابستگی یا dependency injection گفته می شود.
dependency-injection

تزریق وابستگی در جاوا

گفتیم که  تزریق وابستگی یا dependency injection، نوعی الگوی طراحی است که هدف آن، حذف وابستگی بین دو کلاس از طریق ایجاد کد یا کدهای مختلف  است. فرض کنید در زبان برنامه نویسی جاوا بخواهیم یک  تزریق وابستگی انجام دهیم. زمانی که  Class 1 به Class 2  وابسته است، این وابستگی، توسط تعریف فیلدی از Class 2  در Class 1 انجام می شود. پس از آن توسط کلمه کلیدی new ، شی را از Class 2 تولید می‌کنیم.
public class Class1{
public Class2 class2 = new Class2();
}

 

تعریف دقیقی از تزریق وابستگی یا dependency injection

تزریق وابستگی نوعی تکنیک در برنامه نویسی می‌باشد. توسط این تکنیک، هر کلاس به صورت مستقل از وابسته‌هایش فعالیت می‌کند. این کار به وسیله جداسازی ” ساخت شی ” از  ” استفاده از شی ” انجام می‌شود که در نتیجه برنامه نویسان می‌توانند 2 اصل از اصول مهم طراحی شی گرا که اصل وابستگی معکوس یا Dependency inversion principle و اصل تک مسئولیتی یا Single responsibility principle است را به طور کامل در برنامه به کار ببرند.
تزریق وابستگی یا Dependency injection باعث می‌شود که ما به یک معمراری آزاد برسیم و کلاس هایمان کمترین میزان وابستگی را به یکدیگر داشته باشند.
در فرایند تزریق وابستگی، 4 نوع نقش اصلی وجود دارد که در ادامه در مورد هرکدام توضیح خواهیم داد.

انواع بخش‌ها :

شی service : شامل هر شی است که سرویسی را ارائه می‌کند.
شی client : شی client شامل هر شی است که شی service را مورد استفاده قرار می‌دهد.
Interface : Interface در واقع توقعات client از service ها است. Interface به وسیله سرویس اجرا  می‌شود و پس از آن، فرآیند injection به وسیله Interface ها اجرا می‌شود. البته Interface ها را نمیتوان به عنوان کلاس‌های concrete در نظر گرفت چون در داخل Interface، چیزی اجرا نمی شود و Interface ها در سطح Abstract یا انتزاع قرار دارند.
Abstract ، نمایی کلی نسبت به یک شی می‌باشد. به طور مثال زمانی که در مورد یک صندلی فکر میکنیم، صرفا یک شکل کلی را تصور می‌کنیم اما زمانی که به نوع خاصی از صندلی مثلا صندلی چرخدار اشاره میکنیم، دقیقا شکل آن صندلی خاص در ذهن ما نقش می بند. Client معمولا اجازه دریافت هرگونه اطلاعات نسبت به شیوه اجرا سرویس‌های مورد استفاده خود را ندارد. رابط Client و کلاس سرویس،  Interface است.
Injector : توسط Injector ، سرویس‌ها به Client معرفی می شوند. مجموعه Injector ها در زبان‌های برنامه نویسی به شکل پکیج خارجی قرار دارد و نیازی به توسعه Injector توسط برنامه نویس نمی‌باشد.

چرا از تزریق وابستگی استفاده می کنیم؟

قبل از اینکه پاسخ این سوال را بدهیم، لازم است بدانید که نوعی تزریق وابستگی به نام تزریق وابستگی سخت یا Dependency injection hard، این تزریق وابستگی مشکلاتی را ایجاد می‌کند که با گستردگی برنامه، این مشکلات نیز افزایش می‌یابد. مشکلاتی مانند :
  • کاهش یافتن توانایی گسترش دادن برنامه
  • کاهش یافتن توانایی نگهداری
  • کاهش یافتن توانایی استفاده مجدد
  • کاهش یافتن توانایی تست
هدف اصلی تزریق وابستگی، این است که  وابستگی‌ها به صورت مستقل از یکدیگر ایجاد شوند به طوری که کلاس اصلی و یا شی ساخته شده را به آسانی بتوانیم آزمایش یا اجرا کنیم. در صورتی که کلاس در موقع ساخت، از وابستگی‌های خود جدا نشود، در صورت تصمیم برای اعمال هرگونه تغییر در آینده، باید کل کلاس تغییر داده شود.
dependency-injection

روش های استفاده از تزریق وابستگی در زبان جاوا

روش سازنده یا روش Constructor Injection

در روش سازنده، یک شی از کلاس پایین تر به کلاس بالاتری که جنس آن، Interface فرستاده می‌شود.

روش متد یا Method Injection

گفتیم که در روش سازنده، یک شی از کلاس پایین تر به کلاس بالاتری که جنس آن، Interface فرستاده می‌شود. یعنی هر کلاس پایین، لزوما از یک کلاس بالا استفاده می‌کند. اما اگر بخواهیم صرفا یک متد معین را از کلاس برای تزریق وابستگی انتخاب کنیم، باید این وابستگی به یک آرگومان در یک متد مشخص بفرستیم. در این صورت، تزریق وابستگی صرفا بر روی همان متد انجام می شود. به عبارت دیگر، وابستگی از کلاس‌های پایین به متدی در کلاس‌های بالا فرستاده می‌شود.

بیان دقیق تری از تزریق وابستگی در جاوا، در قالب یک مثال

تصور کنید که می‌خواهید در کامپیوتر خودتان، بخشی را ایجاد کنید که در آن، به کاربران پس از ثبت نظر، ایمیل ارسال شود. در این حالت شما کلاسی را خواهید ساخت که توسط آن، این کار برای شما انجام خواهد شد. حالا فرض کنید که بعد از مدتی می‌خواهید، توانایی ارسال sms را نیز اضافه کنید. در این حالت مجبور خواهید بود که کلاس جدیدی را به این منظور بسازید. به عبارت دیگر، کدهای شما مرتبا در حال تغییر هستند. شاید بپرسید آیا می‌شود کاری را جایگزین این تغییر کدها کنیم؟ پاسخ مثبت است. شما با استفاده از تزریق وابستگی در جاوا، قادر خواهید بود این کار را انجام دهید. یعنی شما با داشتن آدرس گیرنده و یک متن از پیش نوشته شده، می‌توانید آن را در قالب ایمیل یا sms به کاربر ارسال کنید.
به این منظور باید توسط تزریق وابستگی در جاوا و همچنین دارا بودن یک رابط کاربری یا Interface، کلاس اصلی را طوری بسازید که بتوانید در صورت اضافه کردن هر تغییر احتمالی، از این کلاس استفاده کنید.
dependency-injection
با یک مثال عملی قضیه را روشن تر می‌کنیم. فرض کنید کلاسی با نام MessageService را در اختیار دارید. وظیفه این کلاس ارسال پیام است. این وظیفه به 2 روش انجام می شود. روش اول که بدون استفاده از DI ( مخفف Dependency Injection ) می باشد و کد نهایی ما به شکل زیر است :
class TypeA{
 sendMessage(){
---------
---------
}
}
 
class TypeB{
 sendMessage(){
---------

 

---------
}
}
class SendMessageApplication{
main(){
 send(type);
}

send(int type){
if(type == 1){
TypeA a = new TypeA();
 a.sendMessage();
}else{
TypeB b = new TypeB();
b.sendMessage();
}
}
}

در این صورت اگر بخواهیم در کلاس A تغییری را انجام دهیم، باید در کلاس C نیز این کار را انجام دهیم. این کار در مورد پروژه‌های ساده، شاید عملی باشد اما در مورد پروژه های پیچیده، این کار بسیار سخت و زمان بر خواهد بود. در این صورت راه حل این مشکل چیست؟ برای حل این مشکل، باید یک رابط کاربری یا interface را بسازیم. این Interface دارای متدی به نام sendMessage می‌باشد و وظیفه آن ارسال پیام است. در این صورت، همه سرویس دهنده‌هایی که در اختیار ما است ( کلاس های ارسال پیام )، لازم است تا این رابط کاربری را implement کنند. به این ترتیب ما کلاس‌هایی داریم که تماما از یک رابط کاربری استفاده می‌کنند. وظیفه‌ی ارسال پیام به صورت انگلیسی و فارسی، بر عهده متد sendMessage است.
در مرحله بعدی باید از طریق تزریق وابستگی، در تابع setter و در کلاس MessageApplication نوع سرویسی ( کلاس های ارسال پیام ) را که مد نظر داریم، گرفته و توسط متد messageService.sendMessage(); که این متد، به تابع sendMessage() مربوط به رابطِ کاربری ما اشاره دارد، برنامه را اجرا کرده و پیام را ارسال کنیم. بنابراین در روش دوم که از تزریق وابستگی، استفاده می‌شود، کلاس نهایی ما به شکل زیر درخواهد آمد :
interface MessageService{

sendMessage();

}

class TypeA implements MessageService{

void sendMessage(){

 //------------

 //------------

}

}

 

class TypeB implements MessageService{

void sendMessage(){

 //-----------

//-----------

}

}

//----------------------------------------------------

class MessageApplication{

MessageService messageService;

void setMessageService(MessageService service){

this.messageService = service;

}

void sendMessage(){

messageService.sendMessage();

}

}

 

پس از این کار، شما قادر خواهید بود تا با ساخت یک شی از کلاس MessageApplication و فراخوانی کردن   sendMessage() ، از 2 روش ارسال پیام ( کلاس های A و B ) بهره ببرید. حتی می‌توانید از انواع دیگر این کلاس‌ها نیز استفاده کنید. بدون اعمال هرگونه تغییر در کلاس MessageApplication ، فقط از نوع جدید آن استفاده کنید.

مزایای استفاده از تزریق وابستگی

تزریق وابستگی مزایای زیادی دارد که در ادامه به برخی از آنها اشاره خواهیم کرد :
  1. در تزریق وابستگی، شما نیازی به compile مجدد کد ها نخواهید داشت و کدها در هنگام اجرا compile می‌شوند.
  2. کاهش یافتن کد‌های مورد استفاده در کلاس
  3. فرایند مربوط به توسعه کدها به شکل آنلاین انجام شده و از آنها در برنامه استفاده می‌شود.
dependency-injection

معایب استفاده از تزریق وابستگی

در کنار مزایای قابل توجه تزریق وابستگی، معایبی نیز وجود دارد که عبارتند از :
1- فرایند مربوط به خواندن کد، بسیار سخت می‌شود. علت این امر نیز قرار نداشتن تمامی کدها در یک بخش و پراکنده بودن آنها در کلاس‌های مستقل از هم است.
2- تزریق وابستگی به تجربه بالایی در کد نویسی و اجرا نیاز دارد. در صورتی که در آن ماهر نباشید، برنامه را بهم خواهید ریخت.
3- مدیریت تزریق وابستگی پیچیده است.

و در آخر

وبسایت میربزرگی قصد دارد تا با ارائه مقاله ها و تجربه‌های کاربردی شما را در زمینه یادگیری و رفع اشکالاتتان کمک کند. در صورت وجود هرگونه سوالی به من ایمیل بزنید.

ارسال دیدگاه

+ 13 = 15

این جا قراره با هم زبان برنامه نویسی جاوا رو یاد بگیریم. اگه جواب سوالتو توی مقاله ها پیدا نکردی، میتونی بهم ایمیل بزنی تا راهنماییت کنم. اگر موضوعاتی رو پیشنهاد داری حتما برام بفرست. منتظر ایمیلتم

پیام با موفقیت ثبت شد.
خطایی رخ داده است.