Управление зависимостями в программном коде
TRANSCRIPT
Управление зависимостямив программном коде
(dependency injection &
Inversion of Control includes)
Agenda
• Качество архитектуры приложения• Что такое зависимый код?• Что такое инъекция зависимости?• Инверсия зависимости• IoC-контейнеры
Это вам знакомо?
• Изменения в одном месте приводят к поломкам в другом
• Методы и классы сложно повторно использовать
• Изменения идут «со скрипом». Трудно что-либо добавить или изменить.
• «Без пол литра не разобраться» или «покажите мне умника, который так пишет?»
Признаки плохой архитектуры
• Хрупкость (изменения ведут к поломкам)• Монолитность (сопротивление к
повторному использованию)• Жесткость (сопротивление к изменениям)• Чрезмерная сложность (ничего непонятно)• Вязкость («грязные» приемы работают
лучше: Copy-Paste, «Объект Бога», «Спагетти код» и т.д.)
Одна из причинСильное связывание кода (Tight Coupling)
Что такое зависимый код?
• public class User {
private MessageService transport = new SmtpMessageService();
public void sendMessage() {
transport.send();}
}
Зависимость!
• public class User {
private MessageService transport = new SmtpMessageService();
public void sendMessage() {
transport.send();}
}
Проблемы с классом User
• Мы не можем изменить реализацию MessageService
• Мы не можем протестировать класс User отдельно от класса SmtpMessageService
• Необходимо создавать новый класс или наследовать от класса User, чтобы добавить новую реализацию.
Как избавиться от зависимости?
• Передать объект через set-метод:public void setMessageService(MessagService messageService) {
this.messageService = messageService;
}
• Передать объект через конструктор:public User(MessageService transport) {
this.transport = transport;
}
Решение найденоИнъекция зависимости
Инъекция!
• Передать объект через set-метод:public void setMessageService(MessagService transport) {
this.transport = transport;
}
• Передать объект через конструктор:public User(MessageService transport) {
this.transport = transport;
}
Программирование основано на интерфейсах
• 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);
}
}
Пример использования
• 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”
Что это нам дает?
• Менее связанный код (low coupling)• Небольшие, сильно зацепленные классы
(high cohesion)• Возможность повторного использования
(reuse)• Расширяемость
Ответственность перешла другому классуИнверсия зависимости
Кто-то должен это делать
• 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”
Ответственность переходит классам верхнего уровня в соответствии принципом инверсии зависимости.
Принцип инверсии
• Зависимости внутри системы стоятся на основе абстракций (интерфейсы или абстрактные классы).
• Модули верхнего уровня не зависят от модулей нижнего уровня.
• Абстракции не зависят от подробностей.
IoC контейнеры
• IoC (Inversion of Control) контейнер – это специальный объект-сборщик, который на основании схемы зависимостей между классами и абстракциями может создать граф объектов. Любой IoC контейнер реализует принцип инверсии зависимостей
• IoC контейнеры появились в Java– Spring– Pico container
• IoC контейнеры используются на самых верхних уровнях приложений для инициализации объектов с учетом всех зависимостей
Java: Spring Framework
• Позиционируется как complete dependency injection tool
• Позволяет описывать зависимости в коде или через XML-файл
Пример для 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();
Хорошая архитектура
• Простота – чем меньше архитектурных решений, тем проще
• Очевидность использования – минимум движений, чтобы получить результат
• Расширяемость – когда есть требования, система легко вбирает в себя функционал
• Устойчивость – разделение ролей позволяет быстро локализировать ошибки
• Повторное использование – низкая зависимость определяет возможности по использованию