Получи случайную криптовалюту за регистрацию!

Чем отличаются Dependency injection, Dependency invertion и In | Java: fill the gaps

Чем отличаются Dependency injection, Dependency invertion и Inversion of Control

Прошлый пост про Liskov, как говорится, "взорвал мой директ", поэтому на этой неделе расскажу про ещё два популярных принципа.

Сегодня про букву D из SOLID — Dependency Inversion. Что это, и чем отличается от Dependency injection и Inversion of Control. Понимание пригодится на собеседованиях, при чтении статей по дизайну и архитектуре.

Будем разбираться на простом примере: класс Service записывает логи в файл через класс FileLogger:

class FileLogger {…}
class Service {
FileLogger logger=new FileLogger();
}

Сделаем код чуть лучше с помощью разных принципов:

Dependency injection
— компоненты создаются не внутри класса, а где-то в другом месте.

Как реализовать: перенести инициализацию логгера в конструктор или сеттер:

class Service {
FileLogger logger;
Service (FileLogger logger) {
this.logger=logger;
}
}

Класс занимается только своей бизнес-логикой
Можно вынести всю конфигурацию в одно место. Или спихнуть часть забот фреймворку, например, Spring

Историческая справка
Когда Spring ещё не был популярен, в проектах использовался паттерн Service Locator.

Суть: компоненты создаются в классе ServiceLocator, а другие классы получают к ним доступ через статические методы:

class ServiceLocator {
static Logger logger = …
static Logger getLogger() {
return logger;
}
}

class Service {
Logger logger=ServiceLocator.getLogger();
}

Dependency invertion (D из SOLID)
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракции

Как реализовать: использовать интерфейс логгера, а не конкретный класс

interface Logger {…}
class FileLogger implements Logger {…}

class Service {
Logger logger=new FileLogger();
}

Интерфейс проще использовать, так как методов меньше
Реализацию легко заменить
Оба класса проще тестировать

Термин "абстракция" используется, потому что SOLID не привязан только к джаве. Группу методов можно выделить в интерфейс, в абстрактный класс и даже в обычный класс. Но интерфейс — наилучший вариант

IoC - Inversion of Control
В маленьких программах жизнь начинается в методе main. Программист создаёт объекты и вызывает их методы, все шаги явно прописаны.

Inversion of Control — это когда ход выполнения программы задаёт фреймворк. Например, Spring создаёт объекты, принимает запросы и не даёт программе завершиться.

Как реализовать: использовать аннотации фреймворка

@Component class FileLogger {…}
@Component class Service {
@Autowired
FileLogger logger;
}

Меньше скучного кода
Низкая связность — код легко читать, менять и тестировать

Резюме:
Dependency injection — класс не создаёт компоненты напрямую, они передаются через конструктор или сеттер
Dependency invertion — класс работает с другими компонентами через интерфейс
Inversion of Control — ход программы задаёт фреймворк

Ответ на вопрос перед постом:
Это словоблудие относится к Dependency injection