February 6, 2020

Dagger 2 для начинающих Android разработчиков — Введение

Данная статья является первой частью серии статей, предназначенных, по словам автора (см. источник), для тех, кто не может разобраться с внедрением зависимостей и фреймворком Dagger 2, либо только собирается это сделать. Оригинал написан 18 ноября 2017 года.

Dagger 2 — это полностью статический фреймворк для внедрения зависимостей в Java и Android (а также в Kotlin), работающий во время компиляции. Dagger 2 — это адаптация созданного ранее компанией Square фреймворка Dagger, поддерживаемая компанией Google.

Для кого эта статья?

Если вы начинающий Android разработчик, изучающий непосредственно Android, то данная статья для вас. Если вы пытались изучать Dagger 2 и то, что вы находили в интернете, казалось вам немного сложным, — не беспокойтесь, я тоже прошел через это (все мы немного особенные, каждому требуется свой подход в объяснении чего-либо) и эта статья определенно для вас. Если вы уже знакомы с внедрением зависимостей и Dagger, то вы сможете узнать что-нибудь новое или прояснить для себя некоторые вещи.

Требования

Предполагается, что вы уже знакомы с языком программирования Java, принципами ООП и Android разработкой.

Что такое зависимость?

Замечание автора: для объяснения данной концепции я буду использовать аналогии с телесериалом «Игра престолов». Если вы не знакомы с данным сериалом, то можете заменять имена классов на более удобные для вас. И вы должны обязательно начать смотреть этот сериал.

Понимание зависимостей и связей — это первый шаг для более четкого понимания концепций объектно-ориентированного программирования. Итак, что такое зависимость? Например, у нас есть класс Targaryens, использующий внутри себя другой класс или интерфейс, который называется Dragons. Значит класс Targaryens зависит от класса или интерфейса Dragons.

Это означает, что класс Targaryens не может работать без Dragons. Также это значит, что везде, где будет использоваться класс Targaryens, будет присутствовать класс Dragons, то есть мы не сможем повторно использовать Targaryens без повторного использования Dragons. В этом случае Targaryens — зависимый класс, а Dragons — это зависимость. Зависимый зависит от своих зависимостей.

Два класса, использующие друг друга, называются связанными (‘coupled’). Связь между классами может быть либо сильной (‘tight’), либо слабой (‘loose’). Зависимости всегда направлены, то есть класс Targaryens зависит от Dragons, но класс Dragons может и не зависеть от класса Targaryens.

class Targaryens {
    init {
        //Каждый раз, когда мы создаем Targaryens, нам необходимо создать и экземпляр Dragons
        val dragons = Dragons()
        dragons.callForWar()
    }
}

Чем плохи зависимости?

Большое количество зависимостей в классе приводит к проблемам сильных связей (hard dependency), что плохо по следующим причинам:

  • Уменьшаются возможности повторного использования кода.
  • Усложняется процесс тестирования.
  • Ухудшается поддерживаемость (maintainability) кода при росте проекта.

Повторное использование (reusablility)

Когда классы и методы слабо связаны, не связаны или не зависят от множества других — возможности повторного использования кода возрастают. Повторное использование кода — одна из базовых идей объектно-ориентированного программирования.

Тестирование

Для тестирования вы можете подменить (mock) конкретные объекты. Но если в классе или методе множество зависимостей, то тестировать его будет сложно. Если класс (например, Targaryens) создаёт экземпляр другого класса (Dragons), то этот класс (Targaryens) невозможно будет протестировать независимо от класса, который был создан в нем (Dragons).

Поддерживаемость (maintainability)

Когда код не может быть корректно протестирован, его части невозможно или сложно повторно использовать, а проект продолжает расти, тогда проект становится очень сложно поддерживать. Поддерживаемость зависит от множества других факторов, но пока новые разработчики в вашей команде понимают систему и делают работу коллег комфортнее, то ваш проект всё ещё поддерживаемый.

Типы зависимостей

Есть множество типов зависимостей, но можно выделить основные:

  • Зависимость от классов.
  • Зависимость от интерфейсов.
  • Зависимость от методов или полей.
  • Прямая и косвенная зависимость.

Зависимость от классов

Мы рассматривали пример зависимости от класса ранее. Конструктор класса Targaryens зависит или нуждается в классе Dragons для вызова метода callForWar() при инициализации.

Зависимость от интерфейсов

В примере ниже метод executePlan принимает интерфейс WarStrategy как зависимость. WarStrategy может быть реализован всеми домами (Targaryens, Starks, Lannisters и так далее).

fun executePlan(strategy: WarStrategy): Result {
    //WarStrategy реализуется всеми домами
}

Зависимость от методов или полей

Следующий метод принимает объект HouseClass, а вызываемый им метод getKing() является зависимостью, которую невозможно идентифицировать в сигнатуре метода.

fun extractName(house: HouseClass): String {
  //возвращает имя короля конкретного дома
  return house.getKing()
}

Прямые и косвенные зависимости

Позвольте мне отвлечься на другой пример. Класс Targaryens зависит от класса Starks в завоевании трона. Но Starks зависят от других королевств севера, например, Mormont. Итак, получается, что Targaryens напрямую зависят от Starks и косвенно от Mormont.

Резюме

Если Java класс создаёт экземпляр другого класса, то этот класс невозможно будет использовать или протестировать независимо от класса, который создан в нем. Это называется зависимостью.

Зависимости плохи тем, что уменьшают возможности повторного использования кода, а также усложняют процесс тестирования, из-за чего поддерживать проект становится сложнее.

Зависимости могут быть различных типов: классовые, интерфейсные, от методов, от полей, прямые, косвенные и другие.

Что дальше?

В следующей статье мы поговорим о решении проблем сильных связей (hard dependency), и да, вы правы, через внедрение зависимостей. Рассмотрим реальный пример, найдем сильную связь и проанализируем её.

Источник: Dagger 2 для начинающих Android разработчиков — Введение