Если тебе приходилось переписывать старый код только для того, чтобы он заработал в новом проекте, значит, ты кое-что упустил. Наш журнал спешит на помощь, срочно выкидывай «костыли» и «ржавые велосипеды» — с этого дня твоя жизнь круто поменяется. 🙂
 

Багаж знаний

Уверен, ты не раз пытался создать уникальное Android-приложение, совместив функциональность нескольких старых проектов, но вот беда — ранее написанные классы никак не хотели работать вместе. Тогда ты начинал их активно рефакторить, переписывая метод за методом. В результате либо получался совсем новый код, либо проект так и не выходил в релиз.

WWW

Если ты присоединился к мощнейшей армии наших читателей недавно, советую тебе прочесть мои предыдущие статьи на тему Android-разработки. Тем более что я часто на них ссылаюсь в новых материалах. 🙂

Причиной всех этих переделок была классическая для ООП проблема связности кода: для функционирования одного объекта требуются два других, а в них нужно инициализировать еще несколько.

public class DoTheJob {
   public doTheJob(Context ctx, String path, Boolean isRecursive …)

Когда класс DoTheJob создавался, все могло быть вполне логично, но теперь его легче полностью переписать, чем адаптировать под новые задачи, — для создания объекта требуется много параметров, причем нет уверенности, что они несут полезную нагрузку.

Теперь придется долго рефакторить код, убирая лишнее, иначе приложение будет съедать слишком много памяти. Совсем недавно мы рассмотрели паттерн MVP, и он, конечно, поможет как-то адаптировать такой код. А иногда новые объекты появляются совсем неожиданно, и за ними тоже надо следить.

AlienClass alien;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 alien = new AlienClass(this);
 ...

Объект alien, возможно, и выполняет полезную работу, но не факт, что она нужна в новом проекте. К тому же ему передается ссылка на Activity, а значит, это потенциальный канал утечки памяти. Придется вникать, для чего этот alien нужен и когда его можно будет уже выгрузить из памяти. Все идет к тому, что на этапе тестирования придется удалить весь старый код и заречься когда-либо его использовать.

Естественно, сложности с повторным использованием кода появились задолго до изобретения ОС Android. Постепенно выработался универсальный подход к решению таких проблем — это паттерн «Инъекция зависимостей», или DI-паттерн.

Как ты уже понял, главная проблема интеграции написанного ранее кода — это необходимость инициализации каких-то других классов, от которых он зависит. Когда в середине кода встречается генерация новых объектов, то считается, что у класса есть зависимость (dependency).

public class A {
   public void doSmth() {
    B b = new B(this);
    b.generateData(); }...

Разрешать такие зависимости можно по-разному, но удобнее всего передать уже существующий объект через конструкторы или set-метод.

public class A {
   public A(B obj)
    {b=obj;}
   public void doSmth()
 {b.generateData(); }...
Рис. 1. Отличие прямой зависимости и ее инъекции
Рис. 1. Отличие прямой зависимости и ее инъекции

Теперь связность кода стала меньше, объект класса B получен в виде ссылки и внутри класса A не нужно заботиться о его генерации. Такой подход называется инъекцией зависимости (dependency injection).

Но есть одна проблема — у класса B могут быть свои зависимости, у которых, в свою очередь, будут еще зависимости. В результате получается цепочка классов, часть из которых нам совершенно не нужна. Сразу возрастает вероятность дублирования объектов, легче «забыть» их в памяти и, как следствие, исчерпать все ресурсы приложения.

К счастью, всё это предусмотрели! Реализуя DI-паттерн, программист должен создать некий контейнер, который будет хранить в себе все необходимые проекту зависимости.

class1 => array("Dependency1", "Dependency2" ...)

И каждый раз, когда потребуется удовлетворить какую-то зависимость, ее можно просто получить из контейнера. При этом контейнер будет не только выдавать объекты, но и следить за их жизненным циклом: не плодить новые без необходимости и выгружать старые, когда они уже точно станут не нужны. Удовлетворение зависимостей кода через умный контейнер — суть DI-паттерна.

 

Dagger 2

Хорошая новость: такой контейнер самому писать не надо. В Java есть несколько реализаций этого паттерна, в мире Android это фреймворк Dagger. Его первая версия появилась несколько лет назад, и он сразу пришелся разработчикам по вкусу. Вскоре компания Google начала развивать его самостоятельно, выпустив сильно дополненную версию, и называется он теперь Dagger 2.

Фреймворк сам построит связи между объектами и будет контролировать их жизненный цикл. В отличие от библиотек и фреймворков, которые мы уже разбирали раньше, Dagger 2 разрешает получившиеся зависимости только на этапе компиляции программы.

 

Структура

В Dagger 2 есть несколько базовых компонентов, на которых будет строиться приложение. Чтобы лучше понять происходящее, коротенько пробежимся по их функциональным возможностям.

  • Singletones (синглтоны, или одиночки) — это все те классы, которые уже были тобой написаны и ждут своей интеграции: сетевые запросы, кеширование и так далее. «Одиночество» заключается в том, что Dagger 2 полностью контролирует их жизненный цикл и не позволяет приложению генерить таких объектов больше, чем необходимо.
  • Modules (модули) — набор классов, описывающих поведение синглтонов. Фреймворк позволяет гибко конфигурировать правила использования объектов, модули нужны именно для этого.
  • Component (компонент) — хранит в себе ссылки на все необходимые части приложения: синглтоны, модули, активити и другие. Фреймворк будет инжектировать зависимости на основе данных, полученных из компонента.
  • Класс Application — не совсем часть фреймворка, но важная составляющая. Сгенерированный код должен функционировать на протяжении жизни всего приложения, от самого старта и до конца. За жизненный цикл приложения в целом отвечает класс Application, его нужно будет расширить и вставить туда запрос на инициализацию фреймворка.

Рис. 2. Схема работы фреймворка Dagger 2
Рис. 2. Схема работы фреймворка Dagger 2

Продолжение статьи доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все статьи на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта, включая эту статью. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи одну статью

Заинтересовала статья, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для статей, опубликованных более двух месяцев назад.


Комментарии

Подпишитесь на ][, чтобы участвовать в обсуждении

Обсуждение этой статьи доступно только нашим подписчикам. Вы можете войти в свой аккаунт или зарегистрироваться и оплатить подписку, чтобы свободно участвовать в обсуждении.

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

В компаниях часто встречается задача уведомлять сотрудников о приближающихся днях рождения…