آموزش الگوی طراحی dependency injection یا تزریق وابستگی

پس از مطالعه این مقاله شما جواب سوال‌های زیر را خواهید دانست.

  • وابستگی سخت یا hard یعنی چی؟ به عبارت دیگه وقتی میگن یه کلاس به کلاس دیگر وابستگی سخت داره این چه معنی‌ای داره
  • چرا باید از dependency injection یا تزریق وابستگی استفاده کنیم؟
  • کتابخانه‌ای که در اندروید برای پیاده سازی dependency injection استفاده میشه چیه؟

قبل از اینکه درباره dependancy injection یا تزریق وابستگی صحبت کنم بهتره موضوع وابستگی رو با ذکر یه مثال توضیح بدم تا متوجه بشیم خود وابستگی به چه معناست. فرض کنید ما یک کلاس به اسم ContactPage داریم که برای صفحه تماس با ما استفاده می‌شود. این کلاس توی برنامه‌ای استفاده شده که کابران از طریق ایمیل میتوانند با پشتیبانی ارتباط برقرار کنند. در این برنامه، کلاس EmailService هم کار فرستادن ایمیل رو انجام میده. کد زیر رو یه نگاه بندازید. کد زیر کلاس ContactPage رو نشون میده که از کلاس EmailService برای فرستادن ایمیل استفاده میکنه.

وقتی داخل یک کلاس از کلاس دیگر استفاده بشود بین آنها وابستگی ایجاد میشه. بنابراین در این مثال کلاس ContactPage به کلاس EmailService وابستگی دارد. وابستگی ها انواع مختلقی دارند. بدترین نوع وابستگی که بهش میگن وابستگی سخت یا hard dependency زمانی ایجاد میشه که با استفاده از new نمونه سازی توی کلاس وابسته انجام بشه. در مثال بالا در کلاس ContactPage یک نمونه از کلاس EmailService با استفاده از کلمه new ساخته شده است در نتیجه وابستگی ایجاد شده از نوع سخت است. وابستگی های سخت وقتی تو برنامه‌ای وجود داشته باشه تغییرات احتمالی آینده رو مشکل میکنه و همین طور تست نوشتن هم سخت میشه.

استفاده از الگوی طراحی dependency injection یا تزریق وابستگی

الگوی طراحی dependency injection یا تزریق وابستگی میگه بیاید به جای اینکه نمونه سازی رو توی کلاس انجام بدید(مثلا با استفاده از new در زبان جاوا) آبجکتی که یه کلاس برای کار بهش نیاز داره رو بیرون از کلاس بسازید و بعد بهش پاس بدید. تو مثال بالا اگه بخوایم dependency injection یا تزریق وابستگی رو اعمال کنیم مثل شکل زیر میشه.

الان ما عملا الگوی dependency injection یا تزریق وابستگی رو پیاده سازی کردیم. ولی برای اینکه کارمون حرفه‌ای تر بشه لازمه یه نکته دیگر رو هم بگم و اون اینه که وقتی دارید الگوی dependency injection یا تزریق وابستگی رو استفاده میکنید باید حواستون به یک اصل مهم توی طراحی شی‌گرا باشه. اون اصل مهم اینه که همیشه استفاده از اینترفیس به استفاده از کلاس ارجحیت داره یا به عبارت دیگه program to interface, not implementation.

الان تو مثال بالا ما این اصل رو رعایت نکردیم و کلاس EmailService رو به ContactPage تزریق کردیم. اگر در آینده تصمیم بگیریم به جای ایمیل از SMS  و یا حتی تماس استفاده کنیم چی میشه؟ با توجه به طراحی الان، مجبوریم کلاس ContactPage رو تغییر بدیم که به جای سرویس ایمیل از یه سرویس دیگه استفاده کنه و این یعنی بالا رفتن هزینه تغییرات. در صورتی که اگر اصلی رو که بالا گفتم رعایت کرده بودیم دیگه لازم نبود با تغییر روش تماس، کلاس ContactPage نیازی به تغییر داشته باشه. خب حالا اگر بخواهیم به جای کلاس از اینترفیس استفاده کنیم و هزینه تغییرات احتمالی آینده رو کاهش بدهیم چطوری میشه؟ یه نگاهی به کدهای زیر بندازید.

همان طور که در کدهای بالا قابل مشاهده است به جای استفاده از کلاس، اینترفیس را به کلاس ContactPage تزریق کردیم. در این صورت، اگر روش تماس با پشتیبانی، در برنامه تغییر کند دیگه لازم نیست کلاس ContactPage رو هم تغییر بدهیم و فقط کافی است که پیاده‌سازی مورد نظر رو به کلاس ContactPage تزریق کنیم. مثل آنچه که در شکل زیر نشان داده شده.

اگر میخواهید حرفه‌ای برنامه نویسی کنید باید همیشه حواستون باشه که وابستگی‌ها رو تزریق کنید. بیشتر هم ترجیح بر این است که وابستگی‌ها از طریق سازنده کلاس تزریق بشوند. حالا این وسط یه مشکلی ایجاد میشه دوباره به کد کلاس Application نگاه کنید ببینید متوجه میشید مشکل کجاست؟ سریع ادامه مقاله رو نخونیداا 🙂

راهنمایی: الان کلاس ContactPage فقط یک وابستگی دارد که یا EmailService بهش تزریق میشه و یا SMSService و خود این کلاس‌ها هم وابسته به کلاس‌های دیگری نیستند و پارامتر ورودی هم نمیگیرند. ولی آیا توی یک پروژه واقعی هم اینطور خواهد بود؟

قطعا در یک پروژه واقعی وابستگی کلاس‌ها به همدیگه زیاد است و این مساله ایجاد یک نمونه از کلاس رو خیلی تو در تو میکنه و باعث میشود که کدها ناخوانا بشوند و کارمون سخت بشه. برای برطرف شدن این مشکل کتابخانه‌هایی وجود دارند که کار تزریق وابستگی رو راحت میکنند. یکی از بهترین کتابخانه‌‌ها برای پیاده سازی dependency injection در اندروید Dagger است که در مقاله بعدی دربارش صحبت میکنیم.

دوستان عزیز هر جا براتون سوال مطرح شد کامنت بزارید خوشحال میشم بتونم پاسخ بدم. شاد باشید 🙂

درباره نویسنده

پست های مرتبط

13 نظر

  1. وحید

    سلام
    رنگ فونتت خوب نیست
    من میخوام یه اپ بنویسم مثل واتس اپ که تماس صوتی و تصویری داشته باشه البته با کاربران خیلی کم مثلا نهایتا ۱۰ نفر همزمان تماس با هم داشته باشن
    بنظرتون از چه زبانی استفاده کنم ؟
    react native خوبه؟

    پاسخ
    1. فهیمه قاسمی
      فهیمه قاسمی

      سلام
      رنگ فونت متن هارو تغییر دادم، فرصت کنم انشالله رو ظاهر سایت کار میکنم. اینکه گفتید کاربران خیلی کم منظورتون رو متوجه نشدم اگه منظورتون اینکه که کلا تعداد کاربرای اپتون کمه مثلا یه پروژه دانشجویی هست در این صورت از react native استفاده کنید

      پاسخ
  2. فرهاد شیری

    با سپاس
    نکات خوبی مطرح کردید، ولی باید به این مسئله که استفاده بیش از اندازه از آبجکتها در تزریق وابستگی به شدت میتونه در کارائی تاثیر گذار باشه! چرا که در زبانهای مدیریت شده ای مانند جاوا ارسال آبجکتها به صورت reference to value یعنی کپی از شی تزریق شده، می باشد بنابراین افراط در این تکنیک میتونه over head زیادی در حافظه داشته باشه والبته فراخوانی های GC هم در افت کارایی تاثیر بسزایی خواهند داشت.
    و نکته بعدی رعایت اصول چک کردن nullability در هنگام ارسال وابستگی ها می باشد، برای همین موضوع در بعضی از شرایط بهتره از tight coupling در تزریق وابستگی استفاده بشه تا در stability سیستم خدشه ای وارد نشه!
    البته استفاده از اینترفیس هایی مانند optional در جاوای ۸ هم تکنیک های خوبی در جهت حفظ پایداری سیستم هست.
    ولی نقطعه ضعف تزریق وابستگی ها بیشتر در اتلاف حافظه که در بالا اشاره شد، مشاهده میشه!

    پاسخ
    1. فهیمه قاسمی
      فهیمه قاسمی

      نکته ای رو که درباره اتلاف حافظه مطرح کردید بنده باهاش موافق نیستم. درسته که جاوا زبان call by value است ولی این مساله منجر به کپی شدن آبجکت و اتلاف حافظه نمیشه. وقتی یک آبجکت به یک تابع پاس داده میشه مقدار خانه ای از حافظه که به آبجکت در heap اشاره میکنه کپی میشه. یعنی فقط مقدار اشاره گر کپی میشه نه کل آبجکت.

      پاسخ
      1. فرهاد شیری

        جاوا دقیقاً مثل C کار می کنه.شما می توانید یک اشاره گر را اختصاص بدهید ، اشاره گر را به یک متد منتقل کنید ، اشاره گر را در متد دنبال کنید و داده هایی را که به آن اشاره شده تغییر دهید. با این حال ، شما نمی توانید در آن مکان اشاره گر اصلی را تغییر بدهید.
        بنابراین اگر چنین کدی داشته باشیم….
        ;Dog d
        دقیقا در C میشه این کد…
        ;Dog *g
        بنابراین اگر کد جاوای زیر را داشته باشیم…
        }(public void foo(Dog
        d = new Dog(“Fifi”); // creating the “Fifi” do
        }

        Dog aDog = new Dog(“Max”); // creating the “Max” dog
        اشاره به آبجکتی که نام مکس داره!
        ;(foo(aDog
        بعد از فراخونی متد با اینکه اشاره گر ارسال شده بود به متد ولی تغییرات روی شی aDog تاثیری نداشت.
        بنابراین فقط آدرس اشاره گر کپی نمیشه! بلکه کل آبجکت کپی میشه در سکشن متد ویا کلاس
        و این یعنی همون overhead

        پاسخ
        1. فهیمه قاسمی
          فهیمه قاسمی

          این کد جاوایی که به عنوان نمونه فرستادین به هم ریخته معلوم نیست چه چیزی انجام شده. روی لینک نمونه کد جاوا کلیک کنید و پروژه نمونه ای که ساختم رو ببینید. توی این پروژه میبینین که چطوری یک آبجکت از نوع Dog به تابعی فرستاده شده و آن تابع روی اون آبجکت تغییر ایجاد کرده.

          پاسخ
  3. وحید نادری

    سپاس از آموزش های خیلی خوبتون و وقتی که اختصاص می دهید
    ممنون که مفاهیم را خیلی خوب انتقال میدید
    منتظر آموزش های خوبتون هستم
    موفق و پایدار باشید

    پاسخ

پاسخ

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

2 × 2 =