Проектирование Android-приложения
Анализ особенностей проектирования Android-приложения. S.O.L.I.D. как акроним для пяти основных принципов объектно-ориентированного программирования и проектирования. Знакомство со списком файлов после реализации абстрактного адаптера для RecyclerView.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 04.12.2019 |
Размер файла | 4,7 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Дополнительный интерес MVVM вызывает, поскольку архитектура, которую предлагают разработчики самой ОС Android очень сильно напоминает MVVM-подход [3]:
Рис. 9. Рекомендованная архитектура для Android-приложения
Более того, разработчики ОС Android даже предоставили несколько готовых реализаций: ViewModel и AndroidViewModel, что увеличивает скорость разработки Android-приложения, применяя именно этот паттерн.
Возможность испускать потоки с данными достигается посредством обёртки для типо LiveData, который также был предложен разработчиками ОС Android. LiveData - это один из ключевых инструментов при реализации MVVM в Android-приложении.
Говоря о MVVM, нельзя оставить без внимания такое понятие как DataBinding, которое в корне отличает данный презентационный паттерн от рассмотренных других. DataBinding - это комопонент, позволяющий напрямую xml-лэйаутам получать данные из ViewModel. Таким образом, больше нет необходимости View связывать приходящие из ViewModel данные с предназначенными им View-элементами в xml-лэйауте.
3.4.2 Преимущества
1. Рекомендована разработчиками ОС Android;
2. ОС Android имеет готовую реализацию ViewModel, позволяющую значительно упростить применения данного паттерна для разработки Android-приложения;
3. Data Binding;
4. Упрощает работу со сложными View.
3.5 Итоги анализа и сравнения
Во-первых, MV* - это презентационные паттерны, ни в коем случае не стоит путать их с функциями и целями, которых применяется The Clean Architecture.
Главные отличия между MVP и MVVM:
? связь один к одному между презентером и View, в то время как View и ViewModel имеют связь один ко многим соответственно [22];
? View имеет ссылку на Presenter, Presenter имеет ссылку на View, а в случае MVVM ViewModel ничего не знает о том, кто будет потреблять её данные [22];
? серьёзное преимущество в пользу MVVM даёт Data Binding.
4. Дополнительные инструменты
4.1 Разрешение зависимостей
4.1.1 Введение в разрешение зависимостей
Поскольку при разработке Android-приложений используется в большей степени объектно-ориентированный подход, то ожидаемо возникает такая ситуация, когда одному объекту для его работы необходим какой-то другой. Допустим, у нас есть класс Университет. Для нормального функционирования «университета» объекту Университет нужен объект Преподаватель. Т.е. можно сказать, что в данном случае объект Преподаватель является зависимостью для объекта Университет, и, таким образом, передача преподавателя каким-нибудь образом внутрь университета является внедрением зависимости или Dependency Injection (DI). Преподаватель тут выступает в роли зависимости, поскольку он необходим для объекту Университет для его работоспособности, а объект Университет зависим от объекта Преподаватель.
На деле это может выглядеть следующим образом:
Рис. 10
Чем плох данный код? Он очень вязкий. Вязкость - отрицательное качество программного кода, выражающееся в пониженной податливости системы для её изменений (changeability) [7]. Представим теперь, что нам нужно заменить преподавателя, для этого нам придётся изменять класс Университет, а это нарушение принципа Open-Closed из SOLID, все принципы которого были разобраны в главе 2 SOLID. Также тут на лицо нарушение ещё одного SOLID принципа, а именно Single Responsibility Principle, - вместо того, чтобы быть сосредоточенным на выполнении своих прямых обязанностей, класс Университет ещё вынужден создавать объекты. И даже несмотря на то, что эти создаваемые им объекты логически с ним связаны, согласно принципу единой ответственности, класс Университет не должен содержать код, ответственный за создание зависимостей.
Наверное, первое, что приходит на ум, чтобы исправить данные недостатки, - передавать зависимости через конструктор или set-метод (сеттер). Тогда код будет выглядеть следующим образом:
Рис. 11
Хочется дополнительно заметить, как разрешение зависимостей тесно связано с SOLID. На самом деле, тут закралось нарушение ещё одного принципа SOLID, но об этом чуть позже.
В основе внедрения зависимостей лежит концепция инверсии управления [6][9]. Инверсия управления подразумевает, что пользователь сам решает, когда взаимодействовать с программой, нежели программа диктует ему подходящий момент. Отличным примером будут консольные программы, которые студенты пишут на 1-2 курсах в рамках дисциплины по программированию. Например, программа для вычисления значения квадратного уравнения может требовать от пользователя последовательно ввести коэффициенты выражения. В случае применения инверсии управления (передачи управления пользователю) программа бы состояла из одного окна, на котором были бы три формы для ввода и коэффициентов и кнопка «Вычислить». Таким образом, пользователь бы имел возможность вводить коэффициенты в любом порядке, поскольку все компоненты (графические элементы: кнопки, поля для ввода и т.д) были бы независимы друг от друга [11]. Т.е. не программа говорит пользователю когда вводить данные, а пользователь сообщает программе, когда их обработать - пользователь управляет программой, а не программа пользователем.
Можно выделить две основные цели техники разрешения зависимостей:
? снятие ответственности за разрешение зависимости с компонента;
? написание более гибкого, повторно используемого кода.
Цели инверсии управления:
? уменьшение зацепления кода (decoupling);
? упрощение расширения приложения (maintainability, easy extendable).
Внедрение зависимостей является частным случаем инверсии управления. Это может быть интерпретировано следующим образом: класс не должен создавать зависимые объекты внутри себя, они должны быть переданы ему извне [4][5][6]. Эта концепция связана с пятым принципом SOLID, - Dependency Inversion (инверсия зависимости). Казалось бы, последний вышеприведённый фрагмент кода уже отвечает данном принципу, однако нет. Принцип инверсии зависимости заявляет о том, что класс должен зависеть скорее от какой-то абстракции, нежели от чего конкретного. То есть наш класс Университет сейчас зависит от объекта, а для удовлетворения данного принципа он должен зависеть от абстракции. Под абстракцией на практике понимают либо интерфейс (чаще), либо абстрактный класс. Возможность передачи интерфейса в функцию как аргумента доступна благодаря полиморфизму, который более подробно обсуждался в главе 2.1 ООП.
Давайте вернёмся к примеру и удовлетворим пятый принцип SOLID, передавая в Университет абстрактный класс Преподаватель:
Рис. 12. Внедрение зависимостей через конструктор
АбстрактныйПреподаватель мог бы быть и интерфейсом, что на практике встречается чаще.
Итак, в примере выше мы не только освобождаем класс Университет от создания объекта, от которого этот класс зависит, передавая зависимость через конструктор. Single Responsibility Principle выполнен. Мы передаём именно абстракцию, тем самым делая тело класса полностью независимым от переданных ему аргументов, что при необходимости позволяет очень легко подменять реализацию, как это и было продемонстрировано. Open-Closed Principle выполнен.
Этот способ близок к идеалу (и даже идеален для некоторых случаев, о котором ниже), но зачем бы было тогда кому-то разрабатывать DI-фреймворки и, вообще, придумывать саму технику внедрения зависимостей? Дело в том, что у этого способа всё же есть пара существенных недостатков [8]. Во-первых, давайте представим, что классу Преподаватель тоже нужен какой-то объект или объекты, т.е. он тоже имеет зависимость. Пусть это будет НаучнаяСтепень, которая, в свою очередь, принимает на вход объект типа СфераДеятельности. И вот мы уже будем иметь следующую конструкцию:
Рис. 13
И если допустить, что объект универ имеет больше одного пользователя, то код для инициализации зависимостей этого объекта будет повторяться, что, в свою очередь противоречит принципу разработки программного обеспечения DRY (англ. Do Not Repeat Yourself, рус. не повторяйся) [10].
Во-вторых, на самом деле от нарушения SRP мы далеко не ушли, поскольку принцип единой ответственности нарушиться позже, когда мы будем инициализировать эти зависимости в каком-нибудь классе.
(Кстати, на последнем приведённом фрагменте кода выше нарушения SRP нет, поскольку функция main() служит точкой входа в программу, т.е. по своей задумке пригодна для любых инициализаций, необходимых для успешной работы программы. Однако, во-первых, ситуация с main() при внедрении зависимостей - это частный случай. Другое дело, что в ОС Android нет единой точки входа на уровне, доступном программисту. Точка входа в приложение находится на очень низком уровне в ActivityThread.main() [12][13]. Приложение может быть запущено разными способами. Самым традиционный - когда пользователь тапает на иконку приложения, а ОС вызывает метод onCreate у Activity, помеченной специальном флагом в манифесте. К менее традиционным способом можно отнести запуск активити посредством Intent или BroadcastReceiver.)
Ну и наконец, чтобы уйти от этих двух недостатков, нужно внедрение зависимостей вынести в отдельный модуль (класс)Данный подход и есть верно выполненное внедрение зависимостей.
Забегая вперёд, данная реализация Dependency Inversion по использованию уже более ближе к паттерну Service Locator, нежели Dependency Injection, но о Service Locator будет рассказано в главе 5.1.4.
Такой подход послужит идеальным решением для маленьких или sample (демонстрирующих какую-то функциональность) проектов, в которых разрешение зависимостей имеет место более одного раза. Соответственно, если зависимости внедряются только единожды, то подойдёт способ, представленный на рис. 9. Для больших проектов использование фреймворка по разрешению зависимостей является предпочтительным. Далее в главе 5.1.5 Service Locator будут даны рекомендации по выбору фреймворка: Dependency Injection или Service Locator. В контексте Android-разработки некоторые разработчики для упрощения зачастую заменяют понятие разрешение зависимостей сразу на внедрение зависимостей. Например, несмотря на то, что, если придерживаться чёткой терминологии, Koin - это Service Locator для разрешения зависимостей, в официальном репозитории на GitHub разработчики для простоты всё-таки называют свою разработку «dependency injection framework» [18].
Далее рассмотрим способы внедрения зависимостей, а также цели (преимущества) данного инструмента.
4.1.2 Способы внедрения зависимостей
Martin Fowler выделяет три способа внедрения зависимостей [6].
Первый - посредством конструктора. Зависимости (объекты) передаются как аргументы в конструктор класса, которому требуются зависимости [как в примере выше]. Данный метод самый безопасный в вопросах многопоточности, однако он не очень гибкий. Например, если вдруг классу, которому внедрили зависимость вдруг потребуется её подменить, то придётся добавлять сеттер, прибегая к методу 2.
Второй - посредством set-метода. Зависимости передаются вызовом set-методов (или одного) у класса, которому они требуются.
Третий - посредством интерфейса. Класс, которому необходимы зависимости, должен реализовать интерфейс, содержащий setter, принимающий зависимость (или несколько). Ответственность за реализацию интерфейса возлагается на программиста, т.е. программисту самому необходимо инициировать реализацию интерфейса (implements в Java), а не ждать, что, например, IDE попросит его об этом.
Если программист занимается внедрением зависимостей самостоятельно, а не с помощью какой-либо библиотеки, то скорее он предпочтёт первый способ, наименее вероятно - последний. На практике же, особенно в больших проектах, программисты, как правило, делегируют внедрение зависимостей фреймворкам. Наиболее популярным фреймворком для внедрения зависимостей в Android-приложениях на момент написания данной работы является Dagger 2, изначально разрабатывавшийся небезызвестной в контексте Android разработки компанией «Square», а впоследствии (начиная с версии 2) поддерживаемый разработчиком ОС Android, - Google.
4.1.3 Преимущества и недостатки
К преимуществам техники внедрения зависимостей можно с уверенностью отнести следующие [4][5]:
? упрощение модульного тестирования. Без внедрения зависимостей модульное тестирование (тестирование классов) по сути невозможно [8];
? сокращение шаблонного кода;
? упрощение поддерживаемости (maintainability) проекта, т.е. расширять проект становятся куда проще;
? сокращения риска того, что придётся изменять класс только из-за того, что одна из его зависимостей изменилась;
? увеличение связности (прочности) кода.
Что касается связности и связанности (сильного зацепления) кода. Модуль имеет какую-то глобальную (объёмную) задачу (SRP), а классы внутри него разбивают эту задачу на более мелкие. Такой подход называется декомпозицией (decoupling). Классы (или методы), полученные после разбиения должны быть логически связанными между собой внутри модуля. Однако сами модули не должны иметь жесткую связь между собой. Например, модуль (класс) RecipesRepository, отвечающий за получения рецептов, может иметь внутри себя методы getItems(), getItemsFromDb(), getItemsFromApi(). Метод getItems() может возвращать данные, исходя из getItemsFromDb(), getItemsFromApi(), например, следующим способом:
Компоненты этого модуля (в данном случае методы) имеют между собой высокую связность. Допустим, есть ещё один модуль (класс) ServerApi, имеющий единственный метод create(). Сильное зацепление (жёсткая связь) между RecipesRepositry и ServerApi должно отсутствовать. При необходимости экземпляр ServerApi может быть инжектирован в RecipesRepository.
Таким образом, методы (или классы) внутри модуля должны иметь высокую связность между собой, однако модули между собой не должно иметь высокую связанность (не должны иметь сильное зацепление). Словосочетание связность кода можно понимать как прочность кода - положительное свойство, в то время как сильную связанность модулей можно понимать как высокую зависимость (сильное зацепление) между ними - отрицательно свойство. Иллюстрация ниже хорошо демонстрирует вышеописанное.
Рис. 14. Связность и связанность (зацепление) кода.
Что касается фреймворков для внедрения зависимостей (DI-фреймворков), несмотря приведённые выше преимущества от использования внедрения зависимостей, в принципе, нельзя сказать, что эти фреймворки (или библиотеки), упрощают работу программиста в короткой перспективе.
Во-первых, безусловно, для использования того или иного фреймворка нужно писать т.н. «повторяемый» boilerplate код. Зачастую, использования паттерна сокращает в 5-10 раз количество кода для разрешения зависимостей. Передавать аргументы во фрагменты не надо. Нет больше необходимости в огромных цепях с передачей файлов по всему проекту.
Во-вторых, например, тот же Dagger 2 и не только подвергаются критике за выполнение своей работы посредством рефлексии и динамического программирования. Это, в свою очередь, служит помехой при использовании функций IDE, направленных на автоматизацию разработки. К таким функциям, например, относятся «Find references», «Show call hierarchy». Известно, что рефлексия и динамическое программирование оба затрудняют безопасный рефакторинг [4][5]. Также внедрение зависимости во время выполнения приложения (в ран тайме, англ. run time) с использованием рефлексии замедляет его работу. В защиту Dagger 2 можно привести аргумент о том, что его разработчики понимают негативные последствия рефлексии и стремятся свести её наличие к минимуму. Например, рефлексия может иметь место, когда сам программист использует Dagger 2 не совсем корректно. Более того, существует библиотека для внедрения зависимостей, не использующая ни рефлексию, ни динамическое программирование, ни прокси. Это Koin, более подробно о котором будет рассказано дальше в главе 5.1.5 DI- фреймворк Koin.
В-третьих, к немаловажным недостаткам использования DI-фреймворков можно отнести следующий: после подключения фреймворка некоторые исключения начинают бросаться в runtime, хотя до этого «всплывали» во время компиляции.
Не будем относить к недостаткам, но оговорим следующую ситуацию. Допустим, у нас есть ArticleFragment, который отображает содержимое какой-то статьи. Также для этого фрагмента у нас есть ArticlesViewModel, которая просто содержит объект со статьями. Возьмём за данность, что объект с сервера нам приходит целиком, т.е. сразу со всеми статьями, поэтому ArticlesViewModel. Таким образом, целесообразным выводом может быть следующий: внедрение зависимостей - вещь очень нужная, каких-то явных недостатков сама по себе не имеющая (если речь не идёт о DI-фреймворках). Поэтому использование данной техники в проектах можно расценивать как необходимое.
4.1.4 Service Locator
Перед тем как перейти к фреймворкам для разрешения зависимостей, стоит упомянуть про Service Locator. Паттерн Service Locator - это ещё один способ, чтобы избавиться от зацепления между модулями; ещё один способ реализации Dependency Inversion Principle. В его основе лежит идея: иметь один объект, который знает, как получить любую зависимость [6]. Такой мы получили, в конечном итоге, в главе 5.1.1.
Основное различие между Dependency Injection и Service Locator заключается в том, что при Dependency Injection класс, которому необходима зависимость сам её запрашивает через конструктор, сеттер или интерфейс. В случае Service Locator, мы бы в этом классе, должны были разрешить эту зависимость, например, так:
ourDependency187 = ServiceLocator.getDependency187()
То есть в случае сервис локатора, объекты, необходимые программе, инициализируются в специальном классе (ServiceLocator в данном случае). Они могут быть инициализированы посредством ленивой инициализации, которая доступна в языке Kotlin (by lazy). Логично, что реализация сервис локатора - это singleton.
Другими словами, Service Locator ничего не знает о том, кому нужны зависимости, он знает как их разрешить. Получается, что объектам, которым нужны зависимости зависят от сервис локатора вместо того, чтобы напрямую зависеть от своих зависимостей. В случае же Dependency Injection, continue
Кстати, Dependency Injection называют контейнерным способом внедрения зависимостей.
У Dependency Injection и Service Locator есть свои преимущества и недостатки, и применять их следуют в разных контекстах. Для кода, который будет использоваться без контроля разработчика, рекомендуется использоваться Dependency Injection. При разработке вы явно указываете какие объекты нужны для работы вашей библиотеки. Один из примеров - разрешение зависимостей для фрагмента. Вы не контролируете, когда фрагмента фрагмент создаётся, отображается или уничтожается, для этого есть колбэки (callbacks). Другой пример - написание библиотеки, которую будут использовать другие разработчики в самых различных проектах.
Паттерн Service Locator лучше подойдёт для кода, который вы всегда и полностью контролируете [11]. Простейшей пример Dependency Inversion посредством свервис локатора был приведён выше. Более продвинутым примером может послужить следующий фрагмент кода:
Здесь используется библиотека Retrofit, реализованная в том числе и с помощью паттерна проектирования «Строитель» (Builder) [14]. Используя данную библиотеку, вы манипулируете фабричными методами, чтобы получить нужный объект (или зависимость). Посредством данной манипуляции вы можете предоставлять зависимости последовательно, чтобы разрешить промежуточные зависимости. Представьте, что некому объекту Object3 нужна зависимость в виде Object2, который, в свою очередь, зависит от Object1. Используя паттерны Service Locator и паттрен «Строитель», мы могли бы написать следующий код:
Рис.15
Резюмируя, простейшая реализация паттерна Service Locator представляет из себя singleton, содержащий все методы для разрешения зависимостей, каждый из которых разрешает конкретную зависимость. Т.е. каждый из этих методов возвращает экземпляр какого-то класса, используемого в приложении. Пример другой реализации - класс, содержащий один метод getService(name: String).
Что касается недостатков паттерна Service Locator, Thorben Janssen в своей статье «Design Patterns Explained - Service Locator Pattern with Code Examples» выделяет следующие [15].
Первый - все компоненты, имеющие зависимости, ссылаются на Service Locator. Данный недостаток, по большому счёту, имеет место только, если эти компоненты используются в разных приложения или в различных окружениях. Иногда в каких-то приложениях или окружениях сервис локатор может «затеряться». Однако данный недостаток можно миновать, реализовав дополнительные интерфейсы, которые абстрагируют этот сервис локатор. Также можно посмотреть в сторону паттерна проектирования «адаптер» [16] для решения этой проблемы.
Второй - из-за того, что Service Locator - это singleton, возникают проблемы расширяемости (scalability) в высокораспараллеленных приложениях.
Стоит отметить, что эти два недостатка имеют место при несколько специфичных условиях в контексте Android-разработки и, при этом, отсутствуют в реализации Dependency Inversion посредством Dependency Injection.
Третий - Service Locator затрудняет тестирование, хотя данное утверждение, в большей степени, зависит от качества реализации сервис локатора. Реализовать Service Locator так, чтобы при тестировании легко манипулировать реализациями, подменяя их на фиктивные, не так просто, но всё-таки возможно. С Dependency Injection это сделать куда легче.
Четвёртый - более высокий риск внесения критический правок или зависимость внешних объектов от реализации интерфейса. Данный недостаток, скорее, относится к проблемам использования интерфейсов и абстрактных классов в принципе, нежели к недостаткам паттерна Service Locator. Более того, наличие этого недостатка может быть следствием нарушения Liskov Substitution Principle - производный объект должен быть в состоянии заменить тот, от которого унаследован, без каких-либо различий в поведении для наружных компонентов [17]. Когда вы реализуете переиспользуемый компонент и используете интерфейс в качестве абстракции, чтобы иметь возможность заменять его реализацию, вы рискуете тем, что при внесении последующих изменений в реализацию этого интерфейса, зависимые от этой реализации внешние компоненты могут перестать работать. Используя интерфейс в качестве абстракции, вы рискуете тем, что при внесении последующих изменений в его реализацию, зависимые от старой реализации внешние компоненты могут перестать работать. Это вынужденная плата, если хотите писать повторно используемый и подменяемый код. Лучший способ избежать данной проблемы - сопровождать интерфейс исчерпывающей документацией, особенно, касаемой его реализации, и написать код, который будет проверять все реализации этого интерфейса на следование контракту. Данный подход помогает обнаружить критические изменения до того, как приложение «упадёт» в рантайме.
Таким образом, с недостатками у паттерна Serviсe Locator всё очень неплохо. К существенным можно отнести лишь третий и, с натяжкой, четвёртый.
4.1.5 DI-фреймворк Koin
Koin зачастую называют сервис локатором, однако сами разработчики позиционируют свой продукт как фреймворк по внедрению зависимостей [18]. Внедрение зависимостей посредством Koin выполняется с помощью ключевых слов. Зависимость можно получить как непосредственно во время выполнения кода - get(), так и посредством «ленивой инициализации» - by viewModel, by sharedViewModel.
Помимо того, что Koin более «лёгкий» фреймворк относительно Dagger 2, Koin ещё и отлично работает с архитектурным компонентом ViewModel, самостоятельно создавая его с помощью ViewModelFactory. И получается, что про последние вовсе можно забыть. В случае с Dagger 2, можно написать generic-метод, создающий ViewModel аналогично способу из Koin.
Ещё одним одной приятной особенностей данного DI-фреймворка является максимально простое создание синглтоново с параметрами.
Давайте посмотрим на пример, в котором вместо семи импортов с помощью Koin получился один, вместо одиннадцати строк кода - одна.
Было:
Стало:
И подобная картина во всех классах, которые имеют зависимости.
4.2 RxJava
Стоит отметить, что в данной главе, по большому счёту, RxJava будет рассматриваться не как многофункциональная библиотека, а то, как её внедрение может укрепить архитектуру Android-приложения, упростить её за счёт общих типов данных.
Таблица. 2. Типы данных в RxJava
Название типа |
Что выпускает |
Наиболее распространённое применение |
|
Observable |
[1:N] или сообщение об ошибке |
получение объектов |
|
Single |
Поток [1] или сообщение об ошибке |
получение объекта |
|
Completable |
Поток без данных содержит (пустой). Если запрос удался - не вернет никаких данных, но выполнится метод onSuccess, в который ничего не будет передано. Если действие не выполнилось - сообщение ошибке. |
отправка статистики / повтор загрузки |
|
Maybe |
[1:n] или ничего, или сообщения об ошибке, если запрос не удался. |
получение объекта, который может отсутствовать |
|
Flowable |
[1:N] или сообщение об ошибке |
получения большого количества объектов / необходимость в backpressure |
Принимая во внимание огромнейший перечень возможностей, который предлагает RxJava, её использование при разработке Android-приложений крайне рекомендовано автором данной работы. Чтобы продемонстрировать одну из таких возможностей, давайте рассмотрим следующую задачу: получить с JSON-файла, находящемся на сервере, две ссылки на изображения и затем сразу же в параллельных потоках скачать изображения по этим ссылкам; когда оба изображения загружены, получить сигнал об этом сигнал; при этом, если любое из действий завершится с ошибкой, то именно оно должно перезапуститься.
Решения на языках Kotlin и уж тем более на Java выглядели достаточно громоздкими, с помощью RxJava эта задача решается в пару строчек:
Вторым преимуществом RxJava является универсальность её типов данных, каждый из которых реализует паттерн Observer. Приведённый выше фрагмент кода очень удобно использоваться во ViewModel, где результат получения данных удобно записать в LiveData, которую предлагает ViewModel. Таким образом, добавляя в MVVM унифицированные типы из RxJava на низкие слои, мы приближаемся к той унитарности, которую отмечали в MVP. Таким образом, опираясь на главу 4, можно заключить, что использование MVVM + RxJava делает архитектурный паттерн MVVM наиболее предпочтительными для разработки Android-приложений. На каком уровни использовать ViewModel, а на каком типы из RxJava, отображено на рис. 11.
Несмотря на то, что, на первый взгляд, LiveData и Rx Java observables очень похожи между собой, т.к. оба реализуют паттерн Observer. От чего может показаться, что они решает одну и ту же проблему, комбинация LiveData и RxJava observables была положительно протестирована автором данной работы в нескольких проектах. Типы данных из Rx прекрасно ложатся на LiveData, а disposable-объекты очень удобно чистить в методе ViewModel onClear(). Таким образом, мы имеем высочайший функционал, дарованный RxJava на слоях ниже View, при этом замечательно пользуемся MVVM паттерном с компонентами, доступными от разработчика ОС Android.
Рис. 16. Комбинирование RxJava и LiveData
5. Реализация прототипа
В качестве архитектуры был выбран паттерн The Clean Architecture и язык программирования Java ввиду их универсальность. Весь код доступен по ссылке: https://github.com/ivan8m8/DailyBudgeting
Программной реализацией бизнес-логики является полностью готовый пакет domain, содержащий в себе Entities, Use Cases, а также repository.
Первоначально был реализован инструмент для удобного управления и переключения между потоками: ThreadExecutorImpl, который реализует интерфейс Executor, имеющий единственный метод execute. Реализация ThreadExecutorImpl гарантирует, что код внутри execute будет выполнен не в основном потоке. Данный инструмент полностью располагается внутри пакета executor. Его реализация вынесена в приложение к данной работе.
В отдельный пакет model вынесена реализация основных сущностей: Cell, MonthDay, FixedExpense, Income, где последние две являются реализацией абстракции AbstractAccount.
UseCases выделены в следующие Interacotrs:
? добавить месячный расход;
? добавить месячный доход;
? изменить месячный расход;
? изменить месячный доход;
? изменить дневные расходы;
? получить элемент (сущность) по id;
? получить список элементов (сущностей ) по году и месяцу;
В свою очередь, из получившихся Interactors выделены следующие абстракции:
? добавить элемент;
? изменить элемент;
? получить элемент;
? получить список элементов.
Более основной целью данных интерфейсов служит не сколько придание гибкости классу с реализацией, сколько наличие в каждой абстракции обратного вызова (callback), сигнализирующего о том, что Interactor выполнил свой сценарий. Данный подход позволяет нам пересекать границы между слоями. Т.е. наш интерактор сможет передать информацию презентеру, ничего про него не зная. Реализация данного подход на примере изменения дневных трат есть в приложении к данной работе.
Каждая реализация интерактора требует экземпляр класс ThreadExecutor, описанного выше. Также каждый интерактор унаследован от базового AbstractInteractor, в который вынесен «каркас» любого интерактора. Это возможность запуска интерактора посредством ThreadExecutor, его отмена, а также получение информации о состоянии: isRunning, isFinished.
Отметим абстрактный метод run(), который должен вызываться напрямую только при тестировании. Он существует только для упрощения тестирования интерактора.
Последним элементом в данном пакете является интерфейс Repository<T>, инкапсулирующий манипуляцию с основными объектами приложения.
Заключение
Целью данной работы было выяснить как разрабатывать такие Android-приложения, в которых можно было бы с лёгкостью и безболезненно заменять или расширять компоненты. Например, сменить фреймворк по работе с базой данных, дупустим, с Postgresql на MySQL, при этом иметь стопроцентную гарантию, что работающие компоненты приложения с предыдущей базой данных не потребуют изменений.
Для этого в контексте разработки Android-приложений были рассмотрены основные принципы объектно-ориентированного программирования, называемыми SOLID. Для каждого из рассмотренных принципов были даны несколько примеров из реальных Android-приложений.
Были также рассмотрены как глобальные архитектурные подходы, так и презентационные паттерны. Стоит отметить, что были найдены некоторые неточности между разными источниками. Помимо прочего, автору данной работы удалось найти применение известной технологии для решения задачи, которая ранее посредством этой технологии не решалась.
Было уделено внимание дополнительными инструментам, укрепляющим архитектуру Android-приложения. К ним относятся разрешение зависимостей путём их внедрения и использования паттерна Service Locator, а также такой инструмент как RxJava, который был рассмотрен не только как инструмент для решения задач, но и как средство, укрпеляющее архитектуру Android-приложения порседством использований универсальных структур данных.
На протяжении всей работы составлялась таблица 1. Требования к архитектуре Android-приложения. Впоследствии на основе удовлетворения требованиям которой были предложены несколько подходов для проектирования архитектуры Android-приложения.
После проведения всех исследований на их основе было спроектировано и затем реализовано Android-приложение. Для проектирования архитектуры этого Android-приложения использовался паттерн The Clean Architecture. Приложение было реализовано на языке программирования Java.
Таким образом, можно заключить, что поставленные цели и задачи для данной работы были достигнуты в полном объёме.
Список использованных источников
1.Clean Architecture // Портал Medium.com [Электронный ресурс]. URL: https://medium.com/@thereallukesimpson/clean-architecture-with-mvvmi-architecture-components-rxjava-8c5093337b43 (дата обращения: 27.11.2018)
2.Refactoring to MVVM // Портал Medium.com [Электронный ресурс]. URL: https://medium.com/@Miqubel/refactoring-to-mvvm-40ebafff43de (дата обращения: 27.11.2018)
3.Jetpack // Официальный портал Android-разработчиков [Электронный ресурс]. URL: https://developer.android.com/jetpack/docs/guide (дата обращения: 27.11.2018)
4.Dependency Injection // Портал Medium.com [Электронный ресурс]. URL: https://medium.freecodecamp.org/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f (дата обращения: 01.12.2018)
5.Краткое введение во внедрение зависимостей // Портал Telegra.ph [Электронный ресурс]. URL: https://telegra.ph/Kratkoe-vvedenie-vo-vnedrenie-zavisimostej-01-14 (дата обращения: 01.12.2018)
6.Dependency Injection // Martin Fowler [Электронный ресурс]. URL: https://www.martinfowler.com/articles/injection.html (дата обращения: 01.12.2018)
7.Вязкость // Портал Wikipedia.org [Электронный ресурс]. URL: https://ru.wikipedia.org/wiki/Вязкость_(программирование) (дата обращения: 12.12.2018).
Размещено на Allbest.ru
...Подобные документы
Архитектура и история создания операционной системы Android. Язык программирования Java. Выбор средства для реализации Android приложения. Программная реализация Android приложения. Проведение тестирования разработанного программного обеспечения.
курсовая работа [167,8 K], добавлен 18.01.2017Архитектура операционной системы Android, набор библиотек для обеспечения базового функционала приложений и виртуальная машина Dalvik. Объектно-ориентированный язык программирования Java как инструмент разработки мобильных приложений для ОС Android.
дипломная работа [1,6 M], добавлен 08.07.2015Современное состояние рынка мобильных приложений. Основные подходы к разработке мобильных приложений. Обоснование выбора целевой группы потребителей приложения. Этапы проектирования и разработки мобильного приложения для операционной системы Android.
курсовая работа [987,1 K], добавлен 27.06.2019Общие характеристики операционной системы Android. Разработка приложения на основе создания менеджера файлов. Получение с помощью приложения доступа к файлам, хранящимся в "облачном хранилище" в сети Интернет. Расчет стоимости программного обеспечения.
дипломная работа [2,7 M], добавлен 03.04.2015Первое устройство, работающее под управлением Android. Приложения под операционную систему Android. Формат установочных пакетов. Разработка приложений на языке Java. Шаблоны основных пакетов и компонентов Android. Сборка приложений, основанная на Gradle.
курсовая работа [492,0 K], добавлен 08.02.2016Общая схема работы приложения Android. Разработка обучающего приложения для операционной системы Android, назначение которого - развитие речи посредством произнесения скороговорок. Описание компонентов разработанного приложения, его тестирование.
дипломная работа [1,2 M], добавлен 04.02.2016Анализ популярных игровых приложений. Жанр – аркады с геймплеем Runner. Получение продукта, ориентированного на людей, использующих мобильные устройства на базе Android, и предназначенный для развлечения пользователей. Визуальная составляющая приложения.
дипломная работа [742,7 K], добавлен 10.07.2017Создание, изучение и разработка приложение на Android. Среда разработки приложения DelphiXE5. Установка и настройка среды программирования. Этапы разработки приложения. Инструменты для упрощения конструирования графического интерфейса пользователя.
курсовая работа [1,6 M], добавлен 19.04.2017Создание приложения, использующего возможности встроенной в ОС Android базу данных SQLite. Проектирование приложения для преподавателей "DataBase". Классы для работы с SQLite. Вставка новой записи в базу данных. Методы update и delete. Листинг программы.
курсовая работа [744,9 K], добавлен 07.07.2014Знакомство с особенностями и этапами разработки приложения для платформы Android. Рассмотрение функций персонажа: бег, прыжок, взаимодействие с объектами. Анализ блок-схемы алгоритма генерации платформ. Способы настройки функционала рабочей области.
дипломная работа [3,4 M], добавлен 19.01.2017Анализ свободно распространяемых систем обучения. Главная контекстная диаграмма (модель AS-IS). Декомпозиция процесса "Регистрация, поддержка пользователей". Выбор методологий моделирования и инструментария. Руководство по установке приложения на Android.
дипломная работа [2,1 M], добавлен 29.07.2016Разработка программного обеспечения для платформы Android версии 2.3: информационное приложения для поклонников футбольной команды, с возможностью просмотра событий, статистики и иной информации о команде и ее успехах. Листинг JsonDataManager.java.
дипломная работа [4,1 M], добавлен 24.04.2013Характеристика работы операционной системы Android, используемой для мобильных телефонов. Создание Android проекта в среда разработки Eclipse. Общая структура и функции файла манифест. Компоненты Android приложения. Способы осуществления разметки.
курсовая работа [1,0 M], добавлен 15.11.2012Средства разработки развивающих и обучающих игр и используемой программы. Среда выполнения и Dalvik. Разработка приложения для платформы Android. Графический интерфейс и обработка касаний экрана. Разработка экранов приложения и их взаимодействия.
дипломная работа [2,1 M], добавлен 18.01.2016Разработка клиент-серверного игрового приложения на примере игры в шашки для мобильных устройств на базе операционной системы Android. Обзор мобильных платформ. Экраны приложения и их взаимодействие. Графический интерфейс, руководство пользователя.
курсовая работа [2,6 M], добавлен 15.06.2013Обзор существующих популярных программ для просмотра погоды на ОС Android. Операционные системы современных смартфонов. Ключевые особенности Android, технология Java. Разработка программной части, выбор языка, описание алгоритма, ее логической структуры.
курсовая работа [911,5 K], добавлен 16.04.2014Разработка приложений для смартфонов на ОС Android для сети аптек "Фармация". Архитектура операционной системы Android. Архитектура и реализация приложения. Его функциональность. Описание работы мобильного приложения. Расчет затрат на создание продукта.
дипломная работа [1,6 M], добавлен 17.06.2017Преимущества операционной системы Android. Проектирование интерфейса приложений. Визуальные редакторы и средства кроссплатформенной разработки. Оптимизация игрового процесса, выбор фреймворка и библиотек. Классификация и характеристика игр по жанрам.
дипломная работа [2,6 M], добавлен 10.07.2017Структура и архитектура платформы Android. Основные достоинства и недостатки операционной системы Android. Среда разработки Eclipse, платформа Java. Подготовка среды разработки. Вкладка "Погода", "Курс валют", "Новости". Просмотр полной новости.
дипломная работа [1,0 M], добавлен 11.07.2014Архитектура операционной системы Android. Инструменты Android-разработчика. Установка Java Development Kit, Eclipse IDE, Android SDK. Настройка Android Development Tools. Разработка программы для работы с документами и для осуществления оперативной связи.
курсовая работа [2,0 M], добавлен 19.10.2014