-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDependencyInjection.txt
154 lines (115 loc) · 10.2 KB
/
DependencyInjection.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Видео на тему: https://www.youtube.com/watch?v=Hm2fTv9wikI
Перевод, исходник: https://habr.com/ru/articles/350068/
****** Внедрение зависимостей ******
Внедрение зависимостей — это стиль настройки объекта, при котором поля объекта задаются
внешней сущностью. Другими словами, объекты настраиваются внешними объектами. DI — это
альтернатива самонастройке объектов. Это может выглядеть несколько абстрактно, так что
посмотрим пример:
******************************************************************************************
public class MyDao {
private DataSource dataSource =
new DataSourceImpl("driver", "url", "user", "password");
//data access methods...
public Person readPerson(int primaryKey) {...}
}
******************************************************************************************
DAO (Data Access Object) класс MyDao нуждается в экземпляре javax.sql.DataSource для того,
чтобы получить подключения к базе данных. Подключения к БД используются для чтения и записи
в БД, например, объектов Person.
Заметьте, что класс MyDao создает экземпляр DataSourceImpl, так как нуждается в источнике
данных. Тот факт, что MyDao нуждается в реализации DataSource, означает, что он зависит от
него. Он не может выполнить свою работу без реализации DataSource. Следовательно, MyDao имеет
«зависимость» от интерфейса DataSource и от какой-то его реализации.
Класс MyDao создает экземпляр DataSourceImpl как реализацию DataSource. Следовательно, класс
MyDao сам «разрешает свои зависимости». Когда класс разрешает собственные зависимости, он
автоматически также зависит от классов, для которых он разрешает зависимости. В данном случае
MyDao зависит также от DataSourceImpl и от четырех жестко заданных строковых значений,
передаваемых в конструктор DataSourceImpl. Вы не можете ни использовать другие значения для
этих четырех строк, ни использовать другую реализацию интерфейса DataSource без изменения кода.
Как вы можете видеть, в том случае, когда класс разрешает собственные зависимости, он становится
негибким в отношении к этим зависимостям. Это плохо. Это значит, что если вам нужно поменять
зависимости, вам нужно поменять код. В данном примере это означает, что если вам нужно использовать
другую базу данных, вам потребуется поменять класс MyDao. Если у вас много DAO-классов, реализованных
таким образом, вам придется изменять их все. В добавок, вы не можете провести юнит-тестирование MyDao,
замокав реализацию DataSource. Вы можете использовать только DataSourceImpl. Не требуется много ума,
чтобы понять, что это плохая идея.
Давайте немного поменяем дизайн:
******************************************************************************************
public class MyDao {
private final DataSource dataSource;
public MyDao(String driver, String url, String user, String password){
this.dataSource = new DataSourceImpl(driver, url, user, password);
}
//data access methods...
public Person readPerson(int primaryKey) {...}
}
******************************************************************************************
Заметьте, что создание экземпляра DataSourceImpl перемещено в конструктор. Конструктор принимает
четыре параметра, это — четыре значения, необходимы для DataSourceImpl. Хотя класс MyDao все еще
зависит от этих четырех значений, он больше не разрешает зависимости сам. Они предоставляются
классом, создающим экземпляр MyDao.
Зависимости «внедряются» в конструктор MyDao. Отсюда и термин «внедрение (прим. перев.: или иначе
— инъекция) зависимостей». Теперь возможно сменить драйвер БД, URL, имя пользователя или пароль,
используемый классом MyDao без его изменения.
Внедрение зависимостей не ограничено конструкторами. Можно внедрять зависимости также используя
методы-сеттеры, либо прямо через публичные поля (хотя внедрение через поля нарушает защиту данных
класса).
Класс MyDao может быть более независимым. Сейчас он все еще зависит и от интерфейса DataSource, и
от класса DataSourceImpl. Нет необходимости зависеть от чего-то, кроме интерфейса DataSource. Это
может быть достигнуто инъекцией DataSource в конструктор вместо четырех параметров строкового типа.
Вот как это выглядит:
******************************************************************************************
public class MyDao {
private final DataSource dataSource;
public MyDao(DataSource dataSource){
this.dataSource = dataSource;
}
//data access methods...
public Person readPerson(int primaryKey) {...}
}
******************************************************************************************
Теперь класс MyDao больше не зависит от класса DataSourceImpl или от четырех строк, необходимых
конструктору DataSourceImpl. Теперь можно использовать любую реализацию DataSource в конструкторе
MyDao.
*** Цепное внедрение зависимостей ***
Пример из предыдущего раздела немного упрощен. Вы можете возразить, что зависимость теперь
перемещена из класса MyDao к каждому клиенту, который использует класс MyDao. Клиентам теперь
приходится знать о реализации DataSource, чтобы быть в состоянии поместить его в конструктор
MyDao.
Вот пример:
******************************************************************************************
public class MyBizComponent{
public void changePersonStatus(Person person, String status){
MyDao dao = new MyDao(
new DataSourceImpl("driver", "url", "user", "password"));
Person person = dao.readPerson(person.getId());
person.setStatus(status);
dao.update(person);
}
}
******************************************************************************************
Как вы можете видеть, теперь MyBizComponent зависит от класса DataSourceImpl и четырех строк,
необходимых его конструктору. Это еще хуже, чем зависимость MyDao от них, потому что MyBizComponent
теперь зависит от классов и от информации, которую он сам даже не использует.
Более того, реализация DataSourceImpl и параметры конструктора принадлежат к разным слоям абстракции.
Слой ниже MyBizComponent — это слой DAO. Решение — продолжить внедрение зависимости по всем слоям.
MyBizComponent должен зависеть только от экземпляра MyDao.
Вот как это выглядит:
******************************************************************************************
public class MyBizComponent{
private final MyDao dao;
public MyBizComponent(MyDao dao){
this.dao = dao;
}
public void changePersonStatus(Person person, String status){
Person person = dao.readPerson(person.getId());
person.setStatus(status);
dao.update(person);
}
}
******************************************************************************************
Снова зависимость, MyDao, предоставляется через конструктор. Теперь MyBizComponent зависит
только от класса MyDao. Если бы MyDao был интерфейсом, можно было бы менять реализацию без
ведома MyBizComponent.
Такой паттерн внедрения зависимости должен продолжается через все слои приложения, с самого
нижнего слоя (слоя доступа к данным) до пользовательского интерфейса (если он есть).