Интерфейсы, внедрение зависимостей и т. Д.

В настоящее время объектно-ориентированное программирование является основой почти всех великих приложений. Благодаря своей большой гибкости, он стал предпочтительным шаблоном проектирования для многих разработчиков.

Одна из вещей, которые делают объектно-ориентированное программирование таким замечательным, - это возможность передавать данные между объектами, и есть много разных способов сделать это. Как разработчик Android, я встречал много новичков, большинство из которых были моими стажерами, которые не умеют правильно общаться между объектами. Это подталкивает их к написанию большого количества шаблонного кода, что затрудняет управление и масштабирование их работы.

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

1. Интерфейсы

Не вдаваясь во все сложные технические детали, в объектно-ориентированном программировании интерфейс - это способ для программиста реализовать различные объекты или иметь возможность использовать одни и те же методы различными способами (метод, также называемый полиморфизмом). Один из лучших способов использования интерфейса - это использовать его для прослушивания изменения состояния объекта и выполнения некоторой задачи или для обнаружения действия и ответа на него, что, как правило, предназначено для связи.

Использование интерфейсов для общения в Android

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

Этот метод с интерфейсами лучше всего подходит для связи между действиями и фрагментами, а также действиями / фрагментами и адаптерами.

Пример списка дел (Активность / Связь с адаптером)

Возьмем, к примеру, приложение со списком дел. Есть TodoActivity, а у этого действия есть RecyclerView, который должен быть заполнен информацией о списке дел из базы данных. RecyclerView может отображать эту информацию только с помощью настраиваемого RecyclerView.Adapter. RecyclerView.Adapter необходимо взаимодействовать с TodoActivity, чтобы прослушивать такие действия, как выбор элемента, и долго нажимать на любой элемент дел, отображаемый в RecyclerView, чтобы TodoActivity мог иметь доступ к элементу в адаптере, когда эти действия происходят.

TodoActivity реализует TodoItemActionListener интерфейс и отменяет все методы интерфейса.

В onCreate() методе TodoActivity RecyclerView правильно инициализирован, а объекты TodoItem получены из базы данных и добавлены в ArrayList<TodoItem>, который будет использоваться для заполнения RecyclerView.

TodoItemsAdapter инициализируется ArrayList, Activity как контекст и Activity как TodoItemActionListener.

Точно так же реализация выше может быть выполнена с помощью Activity / Fragment и Fragment / Fragment. В разделе ниже, посвященном внедрению, я продемонстрирую, как можно передавать или вставлять сложные параметры во фрагмент.

2. Внедрение зависимостей

В объектно-ориентированном программировании внедрение зависимостей - это процесс предоставления ресурсов объекту, который нуждается или зависит от предоставленных ресурсов для выполнения своей предполагаемой цели.

Внедрение зависимостей - это то, что вы делаете постоянно, когда создаете конструктор, который принимает параметры, и когда вы создаете методы установки для переменных в объекте.

В Android внедрение - один из лучших способов передачи информации фрагменту или настраиваемому адаптеру.

Пример списка дел (передача данных в TodoFragment из TodoActivity)

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

В этом примере TodoActivity должен передать или ввести ArrayList<TodoItem> в TodoFragment, чтобы вы могли работать с ним во фрагментах. TodoFragment изменен, чтобы принимать ArrayList<TodoItem> в своем основном конструкторе.

TodoFragment получает ArrayList<TodoItem> инициализацию перед выполнением onCreateView() метода. Если вы хотите использовать ArrayList<TodoItem> для заполнения RecyclerView, вы можете сделать это в методе onViewCreated() фрагмента.

Моя реализация внедрения зависимостей очень проста и предназначена только для объяснения основ. Рассмотрите возможность использования библиотеки Dagger для внедрения зависимостей со всеми лучшими практиками.

3. Одноэлементные классы

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

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

Все классы-синглтоны обычно имеют метод подписи getInstance(), который возвращает статический экземпляр класса-синглтона.

Больше преимуществ

  • Классы-одиночки могут реализовывать интерфейсы.
  • Классы синглтонов могут быть переданы в качестве параметров другим объектам с внедрением зависимостей.
  • С одиночными классами можно обращаться полиморфно, поэтому у вас может быть несколько реализаций.

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

Пример (синхронизация пользовательских данных по множеству действий)

В этом примере пользовательские данные получаются из базы данных в облаке и сохраняются в экземпляре одноэлементного класса с именем UserDataHolder. ActivityA и ActivityB получают доступ к этим пользовательским данным через экземпляр этого одноэлементного класса без необходимости снова запрашивать пользовательские данные из базы данных в облаке и без ActivityA передачи данных в ActivityB через Intent.

В Kotlin упрощено создание одноэлементных классов. Вам просто нужно использовать ключевое слово object при создании класса, чтобы сделать его одноэлементным. Метод init{} инициализирует класс впервые и всегда возвращает единственный экземпляр.

Вызывая методы getUserData() и updateUserData(user:UserModel) в UserDataHolder одноэлементном классе, ActivityA и ActivityB могут с легкостью совместно использовать и обновлять одни и те же данные, что делает взаимодействие оптимальным.

4. Привязка данных

Привязка данных, которую также иногда называют привязкой представления, представляет собой процесс соединения элемента отображения (например, элемента управления или представления пользовательского интерфейса) с информацией, которая его заполняет. Это соединение позволяет автоматически доставлять информацию в требуемое представление или место назначения без использования какого-либо дополнительного кода. Привязка данных - очень распространенная вещь в приложениях, реализующих архитектуру проектирования MVVM (Model View View-model).

Привязка данных обычно заботится о следующем:

  • Отображение актуальных данных.
  • Обработка пользовательских событий.
  • Вызов действий с переменными макета.

Чтобы добавить привязку данных к вашему проекту Kotlin Android, вы должны добавить apply pulgin:'kotlin-Kapt' вверху файла build.gradle уровня приложения вашего проекта, а также добавить data Binding{enable true} в раздел конфигурации Android вашего build.gradle файл уровня приложения.

Пример (привязка представления TodoActivity к ViewModel)

Чтобы реализовать привязку данных в TodoActivity, файл макета activity_todo.xml должен быть преобразован в макет привязки данных. Для этого щелкните правой кнопкой мыши корневой элемент, выберите «Показать действия контекста», а затем «Преобразовать в макет привязки данных». Это преобразует макет в макет привязки данных с разделом <data>, в котором будут размещены переменные макета, которые будут привязаны к представлениям в остальной части макета.

activity_todo.xml имеет TextView с идентификатором status и кнопку с идентификатором update_status. Каждый раз при нажатии кнопки текст в TextView должен изменяться, указывая на обновление статуса. В этом примере мы реализуем эту функцию, используя привязку данных и ViewModel.

Сначала начните с преобразования файла макета activity_todo.xml в макет привязки данных. В Android Studio это легко сделать, щелкнув правой кнопкой мыши корневой элемент, выбрав «Показать действия контекста», а затем «Преобразовать в макет привязки данных».

TodoViewModel содержит все переменные макета, к которым привязан activity_todo.xml, поэтому в разделе <data> activity_todo.xml добавьте:

<data> 
  <variable name="viewModel" type=”com.android101.todolist.data.TodoViewModel”/> 
</data>

Теперь в activity_todo.xml установите значение android:text статуса TextView на android:text="@{viewModel}" и значение android:onClick кнопки update_status на:

android:onClick=”@{()->viewModel.onUpdate()}”

В TodoViewModel переменная состояния, к которой привязан статус TextView, представляет собой наблюдаемую строку, которая будет изменяться каждый раз при нажатии кнопки обновления. Это изменение будет отражено в статусе TextView.

Этот механизм достигается за счет использования LiveData:

val status: LiveData<String> = Transformations.map(_state){                                     it ->  "I can drink $it bottles of Reactor"                           }

Каждый раз, когда пользователь нажимает кнопку обновления, наблюдаемое Int _state увеличивается на единицу, и это вызывает обновление состояния наблюдаемой строки:

fun onUpdate() {                               
_state.value = (_state.value ?: 0) + 1                           
}

Чтобы завершить все, в TodoActivity создайте переменную для TodoViewModel. В методе onCreate() инициализируйте новую переменную привязки, установите ее LifecycleOwner как TodoActivity и установите ее binding.viewModel как свою TodoViewModel переменную.

Результат

Заключение

Связь между объектами в Android бывает многих типов, но секрет создания гладких и бесшовных приложений заключается в применении правильной формы связи между объектами там, где это больше всего необходимо, в соответствии с архитектурой вашего приложения. На этой ноте, удачного кодирования.