Skip to content

4. تزریق وابستگی

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

فرض می‌کنیم که دسترسی به لایه DAO توسط رابط [IArticlesDao] که قبلاً در مورد آن بحث شد، کنترل می‌شود و کل برنامه یک برنامه خرید اینترنتی مبتنی بر وب است. این اقلام توسط لایه [Dao] مدیریت می‌شوند. فرض می‌کنیم دسترسی به لایه کسب‌وکار توسط رابط زیر کنترل می‌شود:

Public Interface IArticlesDomain
     'خرید یک سبد از اقلام
    Sub acheter(ByVal panier As Panier)
     ' بازیابی فهرست اقلام
    Function getAllArticles() As IList
     'بازیابی یک آیتم خاص
    Function getArticleById(ByVal idArticle As Integer) As Article
     'برای ثبت خطاها
    ReadOnly Property erreurs() As ArrayList
End Interface

ما بر روی معنای روش‌های مختلف تمرکز نخواهیم کرد. صرفاً شایان ذکر است که متد [getAllArticles]، که برای بازیابی فهرست تمام اقلام قابل فروش در نظر گرفته شده است، به دسترسی به داده‌ها نیاز دارد. برای به دست آوردن این داده‌ها، باید از رابط [IArticlesDao] فراخوانی کند. اسکلت یک کلاس که رابط [IArticlesDomain] را پیاده‌سازی می‌کند، ممکن است به این شکل باشد:

Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'فیلدهای خصوصی
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

         ' سازنده
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub
...
    End Class
End Namespace

همان‌طور که ذکر شد، برخی از متدهای رابط لایه کسب‌وکار نیاز دارند تا داده‌ها را از لایه [Dao] درخواست کنند. کلاس پیاده‌سازی ما [AchatsArticles] برای رابط [IArticlesDomain] بنابراین به مرجعی به پیاده‌سازی رابط [IArticlesDao] نیاز دارد. در مثال بالا، فیلد خصوصی [_articlesDao] به عنوان این مرجع عمل می‌کند. این مرجع هنگام ساخت یک شیء [AchatsArticles] فراهم می‌شود.

فرض کنید کلاس [AchatsArticles] نوشته شده و می‌خواهیم آن را با یک تست از نوع [Nunit] تست کنیم. بیایید این تست را طوری بسازیم که از یک فایل پیکربندی Spring استفاده کند:

...
Imports Spring.Objects.Factory.Xml
Imports System.IO
...
    <TestFixture()> _
    Public Class NunitSpringTestArticlesDomain

         ' شیء تحت آزمایش
        Private articlesDomain As IArticlesDomain

        <SetUp()> _
        Public Sub init()
       ' بازیابی یک نمونه از سازندهٔ شیء Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' ایجاد شیء DAO «articles» درخواست شده است
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
...
    End Class

محتویات فایل پیکربندی Spring با شناسه [spring-config-domain.xml] چه خواهد بود؟ ممکن است به این شکل باشد:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">

<objects>
    <description>Gestion d'une table d'articles</description>

     <!--کلاسی که رابط IArticlesDao را پیاده‌سازی می‌کند -->
    <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
      <constructor-arg index="0">
            <value>odbc-firebird-articles</value>
        </constructor-arg>
         <constructor-arg index="1">
            <value>SYSDBA</value>
        </constructor-arg>
         <constructor-arg index="2">
            <value>masterkey</value>
        </constructor-arg>
    </object>

     <!--کلاس پیاده‌ساز رابط IArticlesDomain -->
  <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
</objects>

این فایل همان فایلی است که در حال حاضر برای نمونه‌سازی singleton از نوع [IArticlesDao] از لایه [Dao] استفاده می‌شود، که کدی به آن اضافه شده تا singleton از نوع [IArticlesDomain] از لایه کسب‌وکار را نمونه‌سازی کند. این چگونه ساخته خواهد شد؟

  • کد خارجی از Spring یک مرجع به singleton با نام «articlesdomain» در فایل پیکربندی درخواست می‌کند. این مورد برای متد [init] در کلاس تست ما صادق است:
        <SetUp()> _
        Public Sub init()
       'یک نمونه از سازندهٔ شیء Spring بازیابی می‌شود
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
       ' ما ایجاد نمونهٔ شیء DAO «articles» را درخواست می‌کنیم
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
        End Sub
  • اسپرینگ تعریف این سینگلتون مورد نظر را در فایل پیکربندی خود پیدا می‌کند. آن متوجه می‌شود که برای نمونه‌سازی آن، به یک سینگلتون دیگر به نام «articlesdao» نیاز دارد:
  <object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
      <constructor-arg index="0">
        <ref object="articlesdao" />
    </constructor-arg>
  </object>
  • سپس Spring نمونهٔ singleton «articlesdao» را ایجاد می‌کند. ما قبلاً توضیح داده‌ایم که چگونه این کار را انجام می‌دهد.
  • پس از انجام این کار، می‌تواند نمونهٔ واحد «articlesdomain» را ایجاد کرده و مرجعی به آن را به کدی که آن را درخواست کرده بود بازگرداند.

این مکانیزم را می‌توان به شکل زیر نمودار کرد:

می‌توانیم ببینیم که اسپرینگ وابستگی سینگل‌تون «articlesdomain» به سینگل‌تون «articlesdao» را مدیریت کرده است. برای استفاده از اسپرینگ به این روش، ما باید کلاسی با یک سازنده (constructor) ایجاد می‌کردیم که سینگل‌تون وابسته را به عنوان آرگومان بپذیرد:

Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'فیلدهای خصوصی
        Private _articlesDao As IArticlesDao

         ' سازنده
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub
...
    End Class
End Namespace

اصطلاح « تزریق وابستگی » هر دو را در بر می‌گیرد:

  • روش خاصی که کلاس‌های مورد نظر برای ایجاد نمونه بر اساس وابستگی‌هایشان ساخته می‌شوند
  • نحوه مدیریت این وابستگی‌ها توسط Spring هنگام نمونه‌سازی این کلاس‌ها