1. Управление зависимостями
в программном коде
(dependency injection &
Inversion of Control includes)
2. Agenda
• Качество архитектуры приложения
• Что такое зависимый код?
• Что такое инъекция зависимости?
• Инверсия зависимости
• IoC-контейнеры
3. Это вам знакомо?
• Изменения в одном месте приводят к
поломкам в другом
• Методы и классы сложно повторно
использовать
• Изменения идут «со скрипом». Трудно что-
либо добавить или изменить.
• «Без пол литра не разобраться» или
«покажите мне умника, который так
пишет?»
4. Признаки плохой архитектуры
• Хрупкость (изменения ведут к поломкам)
• Монолитность (сопротивление к
повторному использованию)
• Жесткость (сопротивление к изменениям)
• Чрезмерная сложность (ничего непонятно)
• Вязкость («грязные» приемы работают
лучше: Copy-Paste, «Объект Бога»,
«Спагетти код» и т.д.)
6. Что такое зависимый код?
• public class User {
private MessageService transport = new
SmtpMessageService();
public void sendMessage() {
transport.send();
}
}
7. Зависимость!
• public class User {
private MessageService transport = new
SmtpMessageService();
public void sendMessage() {
transport.send();
}
}
8. Проблемы с классом User
• Мы не можем изменить реализацию
MessageService
• Мы не можем протестировать класс User
отдельно от класса SmtpMessageService
• Необходимо создавать новый класс или
наследовать от класса User, чтобы добавить
новую реализацию.
9. Как избавиться от зависимости?
• Передать объект через set-метод:
public void setMessageService(MessagService messageService) {
this.messageService = messageService;
}
• Передать объект через конструктор:
public User(MessageService transport) {
this.transport = transport;
}
11. Инъекция!
• Передать объект через set-метод:
public void setMessageService(MessagService transport) {
this.transport = transport;
}
• Передать объект через конструктор:
public User(MessageService transport) {
this.transport = transport;
}
12. Программирование основано на
интерфейсах
• public interface MessageService {
public void send(String message);
}
• public class SmtpMessageService implements MessageService {
public void send(String message) {
System.out.println(“Via smtp: ” + message);
}
}
• public class JabberMessageService implements MessageService {
public void send(String message) {
System.out.println(“Via jabber: ” + message);
}
}
13. Пример использования
• User userWithSmtp = new User();
userWithSmtp.setMessageService(new SmtpMessageService()) ;
userWithSmtp.send(“you can do it”); // print “Via smtp: you can do it”
• User userWithJabber= new User();
userWithJabber.setMessageService(new SmtpMessageService()) ;
userWithJabber.send(“ yes, you can”); // print “Via jabber: yes, you can”
14. Что это нам дает?
• Менее связанный код (low coupling)
• Небольшие, сильно зацепленные классы
(high cohesion)
• Возможность повторного использования
(reuse)
• Расширяемость
16. Кто-то должен это делать
• User userWithSmtp = new User();
userWithSmtp.setMessageService(new SmtpMessageService()) ;
userWithSmtp.send(“you can do it”); // print “Via smtp: you can do it”
• User userWithJabber= new User();
userWithJabber.setMessageService(new SmtpMessageService()) ;
userWithJabber.send(“ yes, you can”); // print “Via jabber: yes, you can”
Ответственность переходит классам верхнего уровня в соответствии
принципом инверсии зависимости.
17. Принцип инверсии
• Зависимости внутри системы стоятся на
основе абстракций (интерфейсы или
абстрактные классы).
• Модули верхнего уровня не зависят от
модулей нижнего уровня.
• Абстракции не зависят от подробностей.
18. IoC контейнеры
• IoC (Inversion of Control) контейнер – это специальный
объект-сборщик, который на основании схемы
зависимостей между классами и абстракциями может
создать граф объектов. Любой IoC контейнер реализует
принцип инверсии зависимостей
• IoC контейнеры появились в Java
– Spring
– Pico container
• IoC контейнеры используются на самых верхних уровнях
приложений для инициализации объектов с учетом всех
зависимостей
19. Java: Spring Framework
• Позиционируется как complete dependency
injection tool
• Позволяет описывать зависимости в коде
или через XML-файл
20. Пример для Spring Framework
• XML файл описания (dependency.xml)
<beans>
<bean id=“messageService" class="com.my_app.StmpMessageService"/>
<bean id="client" class="com.my_app.User">
<property name=“transport">
<ref bean="messageService"/>
</property>
</bean>
</beans>
• Получение объектов с учетом зависимостей
BeanFactory factory = new XmlBeanFactory(new
FileInputStream("dependency.xml"));
User user= (User)factory.getBean(“user");
user.send();
21. Хорошая архитектура
• Простота – чем меньше архитектурных решений, тем
проще
• Очевидность использования – минимум движений, чтобы
получить результат
• Расширяемость – когда есть требования, система легко
вбирает в себя функционал
• Устойчивость – разделение ролей позволяет быстро
локализировать ошибки
• Повторное использование – низкая зависимость
определяет возможности по использованию