Интерфейсы, внедрение зависимостей и т. Д.
В настоящее время объектно-ориентированное программирование является основой почти всех великих приложений. Благодаря своей большой гибкости, он стал предпочтительным шаблоном проектирования для многих разработчиков.
Одна из вещей, которые делают объектно-ориентированное программирование таким замечательным, - это возможность передавать данные между объектами, и есть много разных способов сделать это. Как разработчик 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 бывает многих типов, но секрет создания гладких и бесшовных приложений заключается в применении правильной формы связи между объектами там, где это больше всего необходимо, в соответствии с архитектурой вашего приложения. На этой ноте, удачного кодирования.