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>
Цей файл — це той самий, що вже використовувався для створення екземпляра синглтона типу [IArticlesDao] з шару [Dao], до якого було додано код для створення екземпляра синглтона типу [IArticlesDomain] з бізнес-шару. Як саме він буде побудований?
- Зовнішній код запитує у Spring посилання на синглтон із назвою «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
- Spring знаходить у своєму конфігураційному файлі визначення відповідного синглтона. Він виявляє, що для його інстанціювання йому потрібен інший синглтон під назвою «articlesdao»:
<object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
- Тоді Spring створить екземпляр синглтона «articlesdao». Ми вже пояснювали, як це відбувається.
- Після цього він може створити екземпляр синглтона «articlesdomain» і передати посилання на нього коду, який його запросив.
Цей механізм можна схематично зобразити так:
![]() |
Бачимо, що Spring впорався із залежністю синглтона «articlesdomain» від синглтона «articlesdao». Щоб використовувати Spring таким чином, нам довелося створити клас із конструктором, який приймає як аргумент залежний синглтон:
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 керує цими залежностями під час створення екземплярів цих класів

