Проектирование Android-приложения

Анализ особенностей проектирования Android-приложения. S.O.L.I.D. как акроним для пяти основных принципов объектно-ориентированного программирования и проектирования. Знакомство со списком файлов после реализации абстрактного адаптера для RecyclerView.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 04.12.2019
Размер файла 4,7 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Введение

Основная цель данной работы - предложить способ разработки легко-поддерживаемых и легко-расширяемых Android-приложений. Для этих целей были решены следующие задачи: проанализированы основные принципы объектно-ориентированного программирования (SOLID), были рассмотрены несколько способов применения этих принципов именно в контексте Android-разработке. Что касается применения популярных архитектурных паттернов, обсуждались как глобальные архитектурные подходы, так и презентационные паттерны. Также были рассмотрены дополнительный инструменты, укрепляющие архитектуру Android-приложения. На всём протяжении выполнения данной работы составлялась таблица с требованиями к архитектуре Android-приложения. Применяя один из рассмотренных архитектурных паттернов, было спроектировано и разработано Android-приложение.

Выполняя ВКР на 4 курсе, я чувствовал, что написанный мной программный код в рамках этой работы, мог бы быть лучше. Я абсолютно понимал, что из-за его монолитности, его будет трудно поддерживать: реализация нового или расширение уже имеющегося функционала могли бы привести к прекращению функционирования предыдущего. Это очень важно учиться на своих ошибках, поэтому было принято решение о написании работы именно на данную тему.

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

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

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

1. SOLID

1.1 Общие сведения

S.O.L.I.D. - это акроним для пяти основных принципов объектно-ориентированного программирования и проектирования. Автором самих принципов является Robert «Uncle Bob» Martin, в то время как автор акронима - это Michael Feathers, объединивший эти принципы в начале 2000-ых.

S.O.L.I.D. помогает бороться со следующими проблемами, возникающими при разработке ПО [26]:

? сильное зацепление (связанность или вязкость) и неустойчивость. Изменение в проекте затрагивает другие компоненты, которые не связаны с вносимым изменением (эффект «снежного кома»). Таким образом, система с трудом поддаётся изменениям, а также может разрушаться в тех местах, непосредственно к которым изменение не относилось;

? неподвижность. Систему трудно разделить на компоненты, например, для возможности их переиспользования;

? трудность поддержки. Система состоит из трудно читаемого, не до конца понятного кода даже его автору, соответственно, новому программисту в команде разобраться в коде будет неоправданно сложно.

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

1.2 Принцип единой ответственности

Данный принцип можно сформулировать следующим образом: «один класс или одна функция должны решать только одну проблему». Оригинальное определение звучит так «A class should have only one reason to change», что означает - может возникнуть только одна ситуация, при которой вам необходимо изменить какой-то конкретный класс, т.е. за каждым классом закреплена одна ответственность; один класс решает одну проблему. Более того, несмотря на то, что в оригинальном определении речь идёт о классе, этот принцип может быть применён и к функции. Ниже, как раз-таки, на примере функции и будет продемонстрировано применение принципа единой ответственности.

Возьмём один из основных компонентов Android SDK RecyclerView. RecyclerView - достаточно гибкий компонент, отображающий список каких-то данных. Для того, чтобы ему передать данные, необходим адаптер. Для RecyclerView в Android SDK есть уже RecyclerView.Adapter. Переопределяя его основные методы, мы можем ускорить нашу работу с RecyclerView. Один из таких методов - это onBindViewHolder(). Его ответственность - привязывать («маппить») данные объекта к соответствующему компоненту пользовательского интерфейса. Давайте сразу взглянем на код с типичной ошибкой:

В приведённом выше фрагменте кода [27] в onBindViewHolder() происходит не только маппинг объекта к соответствующим View, но и ещё два действия: вычисление конечной суммы заказа, а затем конвертация этой суммы в доллары. Такой подход, очевидно, нарушает принцип единой ответственности. Вычисления и конвертация должны быть вынесены в отдельный класс. Однако тут есть небольшие нюансы - на самом деле, не всё так однозначно, и в дальнейшем мы разберём почему. Тем не менее, нарушение есть, давайте сначала разберёмся почему вычислесления и конвертация внутри onBindViewHolder() делают код хуже.

Основная проблема представленного выше кода заключается в том, что если в дальнейшем нам где-то ещё потребуется применить точно такие же операции - вычислить конечную сумму заказа или конвертировать её в доллары - то нам придётся писать точно такой же код ещё раз в другом месте. Отсюда может получиться такая ситуация: например, нам вдруг понадобилось поменять логику вычисления конечной суммы заказа или вдруг понадобилось конвертировать эту сумму в рубли, а не в доллары, как раньше - в итоге, мы меняем необходимую логику в одном месте, но забываем её заменить в другом. Страшно то, что далеко не факт, что подобная ошибка будет замечена при тестировании; есть риск узнать о ней только от пользователя, поставившего единичку в Google Play. Чтобы избежать таких ситуаций, необходимо придерживаться принципа DRY (Don't Repeat Yourself) [28].

Donn Felker ещё отмечает, что при внесении изменений в UI элемента списка, который отображается в RecyclerView, нам придётся вносить изменения и в сам адаптер [27]. На мой взгляд, если подобное воспринимать как проблему, то её решение, с какой-то стороны, можно отнести к оверинжинирингу.

Касательно данного ошибочного подхода можно ещё добавить, что имея ошибку в логике вычисления конечной суммы заказа или в логике приведения её в доллары, при автоматизированном тестировании метода onBindViewHolder() мы получим ошибку, хотя сам «биндинг» может быть выполнен корректно. То есть ошибка будет казаться нелогичной.

Избежать нарушения принципа единой ответственности в данном примере очень легко - достаточно логику вычисления конечной суммы заказа вынести в Order.getOrderTotal(), а конвертацию куда-то ещё, например, в отдельный класс Utils или какой-нибудь CurrencyFormatter, причём так, чтобы метод order.getOrderTotal() возвращал сумму всех заказов в нужной валюте. Т.е. вычисление конечной суммы заказа внутри этого метода будет выполняться непосредственно, а для конвертации использоваться метод вроде Utils.convertCentsToDollars(total). Стоит отметить, что данный метод очень желательно покрыть тестами, чтобы во время ошибки в order.getOrderTotal(), мы точно знали, что ошибка именно внутри получения общей суммы заказа, а не при конвертации. В конечном итоге метод onBindViewHolder() примет следующий, правильный вид:

Теперь поговорим об упомянутых выше нюансах. Опираясь на Single Responsibility Principle, давайте взглянем на сам RecyclerView.Adapter. К его ответственностям относятся: создание и наполнение ViewHolder, привязка данных к ViewHolder, вычисление количества элементов и ещё некоторые. То есть мы видим больше одной ответственности, но значит ли это, что RecyclerView.Adapter «из коробки» нарушает принцип единой ответственности? Ответ: нет.

Во-первых, RecyclerView.Adapter представляет собой реализацию структурного паттерна проектирования adapter [16]. Поэтому всё изобилие его ответственностей: создание, наполнение, привязка модели к ViewHolder и прочие - идёт от абстракции.

Во-вторых, принцип единой ответственности не так прост, как может показаться на первый взгляд. Необходимо умение отличать когда этот принцип применять необходимо, а когда им следует пренебречь. Самое главное - определить, могут ли те части, которые мы хотим отделить, быть переиспользуемы по отдельности; если метод или часть класса никогда не будут использованы отдельно, то отделять их не нужно. Таким примером и является RecyclerView.Adapter. Вне адаптера нам никогда не понадобится ни одна из его ответственностей, т.е. нам никогда не понадобится, например, привязывать модель к ViewHolder где-нибудь в Activity и тому подобное.

Таким образом, можно заключить, RecyclerView.Adapter принципа единой ответственности не нарушает, его ответственность инкапсулирована в несколько методов, которые вне адаптера не имеют смысла.

Давайте теперь, опираясь на вышесказанное, немного усложним задачу: когда order.getOrderTotal() вернёт 0.0, нужно вместо нуля отображать картинку с яркой надписью «FREE». Таким образом, у нас появляется новая логика. Так как она относится к пользовательскому интерфейсу, а не к бизнесу, манипулировать методом order.getOrderTotal() - плохая идея, да и возвращать разные типы метод с одинаковыми аргументами в Java или Kotlin не может.

Казалось бы, остаётся только один вариант - адаптер. Помимо метода onBindViewHolder(), ответственность которого, как мы ранее выяснили, заключается только в том, чтобы привязывать модель к пользовательскому интерфейсу, в RecyclerView.Adapter есть такой метод как

onCreateViewHolder(parent: ViewGroup, viewType: Int),

ответственность которого - создавать вью холдер. В нашем случае, это то, что нужно. Ключевым для нас станет аргумент viewType.

Идея решения такая: реализовать новый идентичный ViewHolder, но только для показа конечной суммы заказа вместо TextView использовать ImageView, и затем в зависимости от того, какой придёт аргумент viewType, возвращать нужный ViewHolder. Так как типом аргумента viewType является Int, у нас есть возможность внутри метода onCreateViewHolder() сразу принимать нужный layout в виде R.layout.layout_we_need, что просто удобно. Для этого необходимо переопределить ещё один метод адаптера - getItemViewType(position: Int), который будет выглядеть следующим образом:

Однако нетрудно заметить, что при данном подходе, если в будущем мы внесём изменения во View (например, наоборот откажемся от ImageView), нам вновь придётся вносить изменения в адаптер. С одной стороны, на лицо сильное зацепление, с другой - мы уже отмечали достаточно не единичную ответственность адаптера. Тут уже каждый должен решить сам, оценив насколько в будущем будут вероятны изменения во View, будет ли реальная польза от абстрактного адаптера и т.д.

Что касается абстрактного адаптера. Можно предложить два варианта его проектирования. Традиционный - создать такую абстракцию адаптера, которая

1. смогла бы работать с любыми

a. данными;

b. вью холдерами;

c. мульти типами View у одного элемента списка (когда у адаптера переопределён метод getItemViewType()).

2. содержала реализацию методов, которая неизменна для всех адаптеров, например,

override fun getItemCount() = items.size()

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

Рис. 1. Список файлов после реализации абстрактного адаптера для RecyclerView

Второй способ - использовать презентационный паттерн. То есть у каждого адаптера будет свой собственный controller / presenter / ViewModel, который внутри будет содержать всю специфичную для своего адаптера бизнес UI-логику. Такой подход достаточно экзотический, на практике я такого не встречал. Да и в целом, его польза сомнительна. По факту мы получим один общий адаптер ту же кучу адаптеров только вынесенных в контроллер.

Уместно отметить, что на официальном сайте по Android-разработке можно встретить предостережение касательно увеличения количества абстракций. Рекомендуется не злоупртерблять абстракциями и применять их там, где от них нет явной пользы. Дело в том, что большое количество абстракций помимо того, что отнимает большее время на их разработку, так ещё и может замедлить работу приложения, поскольку занимает лишние ресурсы [29]. Давайте рассмотрим ещё один, более сложный практический пример: у нас есть выпадающий список, состоящий из какого-то количества групп и выпадающих из них объектов. Как объект, так и всю группу целиком, можно выбрать с помощью чекбоксов. Более того, есть ещё общий чекбокс, позволяющий выбрать сразу все элементы и, соответственно, отменить выбор сразу всех элементов. У общего чекбокса и чекбокса для группы должны быть три состояния: выбран, не выбран, частично выбран. В зависимости от того какие объекты выбраны, на том же экране на карте необходимо отрисовать их координаты. GUI выглядит следующим образом.

Рис. 2. GUI Android-приложения X-Keeper

приложение проектирование адаптер

Чтобы адаптер этого выпадающего списка отвечал SRP, вся вышеописанная логика не должна в нём присутствовать. Адаптер лишь на основе приходящих ему данных должен «смотреть» в каком состоянии находится тот или иной чекбокс и нужное состояние отображать. Нажатие на чекбокс так же не обрабатывается внутри адаптера непосредственно, а передаётся контейнеру внутри которого этот адаптер выполняется. (К слову, контейнер так же всё ещё не обрабатывает это событие, а передаёт его ниже.) Таким образом, вся бизнес UI логика данного кейса должна быть вынесена в специальный контроллер. Контроллер - это часть какого-то MV* презентационного паттерна, которые будут подробно рассмотрены внутри главы 3. Архитектурные паттерны. В данном случае, этот контроллер и будет на основе выбранных элементов управлять картой.

Заметим, что если в предыдущем примере решение использовать ли абстрактный адаптер было не до конца обоснованными, то в данном примере, оставь всю бизнес-логику UI в адаптере, мы бы получили адаптер, который управляет целым фрагментом или активити.

Ещё до того как принимать решение о необходимости применения SRP, стоит отметить, что не всегда видны потенциальные ситуации, где его следовало бы применить. Часто вы смотрите на код очень «близко» или работаете над классом так долго, что он вам кажется совершенным и, кажется, что применять SRP не к чему. В данной ситуации уместна такая пословица: «Из-за деревьев леса не видно». То есть чтобы увидеть лес, нужно из него выйти. Проецируя на нашу ситуацию: чтобы обнаружить потенциальные места для применения SRP, нужно попытаться «отодвинуться» от своего кода, посмотреть на него со стороны и более глобально, не вдаваясь в детали.

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

Принятие решения о том, следовать SRP или нет зависит от того, насколько сильно и в каком направлении ваше приложение будет меняться со временем. Если проект небольшой или его модернизация не планируется, то тратить много времени на разделение логики не следует.

Резюмируя, Single Responsibility Principle, на первый взгляд кажется достаточно простым принципом, однако вопрос о необходимости его применения на практике становится настоящей дилеммой. Излишнее применение SRP добавит проекту больше сложности, чем пользы. Выше мы уже определили главное правило, помогающее понять необходимо ли применять SRP в данном случае: если функция или класс не могут или не планируются быть переиспользуемыми, то применением SRP следует пренебречь; во всех иных ситуациях следование принципу единой ответственности заметно укрепит архитектуру приложения.

Следование принципу единой ответственности позволяет:

? уменьшить частоту, с которой необходимо изменять класс;

? сделать архитектуру приложения более крепкой.

1.3 Open-Closed Principle

приложение проектирование адаптер

Open-Closed Principle или же принцип открытости-закрытости повествует следующее: сущности программного обеспечения (функции, классы, модули) должны быть открыты для доработки, но закрыты для непосредственной модификации. Если взять, например, класс, то мы можем дорабатывать, переопределять и даже изменять логику его методов, но делать мы это должны в уже новом унаследованном от исходного классе. Фраза «но закрыты для непосредственной модификации» означает, что напрямую исходный класс не должен редактироваться при расширении его функционала. Естественно, это же касается и функций, и модулей.

Принцип открытости-закрытости просит нас отказаться от переписывания уже работающего и где-то использующегося кода лишь для того, чтобы переиспользовать его в каком-то новом месте. Если при реализации нового функционала мы видим, что можем воспользоваться каким-то уже существующим классом, однако для этого его нужно дополнить или изменить, пусть даже немного, мы всё равно должны создать новый класс. Причём мы должны не копировать туда содержимое старого класса, а унаследоваться от него и дополнить его необходимым функционалом, переопределяя уже имеющиеся методы или добавляя новые. Таким образом следование Open-Closed Principle не позволяет нам изменять уже работающий, протестированный функционал, тем самым способствуя тому, что добавление нового функционала не приведёт к прекращению работы старого.

Для того, чтобы следовать данному принципу, на помощь приходят такие инструменты в Java/Kotlin как полиморфизм и, в большей степени, наследование. Особенно это носится к первой части определения.

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

Давайте рассмотрим такой пример: допустим, какое-то время назад была задача написать приложение, которое рисовало бы геометрические фигуры: квадрат, круг, треугольник. В скором времени для каждой из этих фигур мы спроектировали дата классы, реализовали приложение и залилили его в продакшен, где оно успешно отработало несколько месяцев. Теперь стала новая задача: необходимо вычислять площадь всех отрисованных фигур [31]. Таким образом, хорошо было бы иметь какой-то метод, которому на вход подаются фигуры, и он рассчитывает площадь каждой и затем суммирует эти площади, возвращая результат. Вспоминая только что рассмотренный принцип единой ответственности, очевидно, что такой метод может пригодиться не в одном месте внутри программы, поэтому верным подходом будет создать класс AreaManager и внутри него нужный нам calculateArea(shapes: ...).

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

Более того, у этого метода есть ещё одна достаточно серьёзная проблема. Дело в том, что изначально мы не задумывались о наследовании, поэтому имеющиеся дата классы для геометрических фигур не имеют ничего общего, что вынуждает нас проектировать функцию таким образом, что она принимает на вход всё, что угодно. И хотя если ей на вход придёт неопознанный тип, то будет брошено исключение и приложение упадёт, но упадёт оно именно в рантайме, а не на стадии компиляции. То есть нет гарантии, что строчка throw new RuntimeException(...) сработает до того, пока приложение с багом не попадёт в продакшен, что ставит заключительное клеймо на данном подходе о его непригодности.

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

Вопреки названию данного раздела давайте начнём со второй проблемы и для её решения воспользуемся наследованием и полиморфизмом. Можно создать абстрактный класс Shape, который будет содержать все общие аспекты геометрических фигур, однако кроме того, что каждая из них занимает какую-то площадь, на данном этапе трудно выделить что-то ещё. Поэтому пусть наш Shape будет не абстрактным классом, а интерфейсом, содержащим единственный метод getArea().

public interface Shape {

double getArea();

}

Затем каждый дата класс геометрической фигуры должен будет реализовать данный интерфейс и в методе getArea() переопределить вычисление площади конкретной фигуры.

Сделав такое несложное действие, мы получаем сразу несколько преимуществ. Во-первых, уже очевидно, что в классе AreaManager больше не будет логики с вычислением площади каждой фигуры, а значит мы полностью уходим от того огромного количества веток else и проверок типа instanceof. Теперь в методе calculateArea() нам просто необходимо вызвать метод getArea() у каждой фигуры и сложить возвращаемые им значения.

И что самое главное, нам больше не придётся каждый раз при добавлении в программу новой геометрической фигуры, редактировать класс AreaManager, из чего следует, что Open-Closed принцип соблюдён.

Во-вторых, метод calculateArea() теперь принимает на вход список не с чем угодно, а только с такими объектами, которые реализуют интерфейс Shape. Если вдруг кто-то попытается передать неподдерживаемый тип, то приложение со стопроцентной гарантией упадёт во время компиляции, а не во время рантайма.

И, наконец, в-третьих, при новом подходе, написанный код куда легче поддерживать и читать. При добавлении новой фигуры, нам не нужно волноваться о том, чтобы уже написанный AreaManager.calculateArea() корректно с ней работал, его ответственность сведена к минимуму - суммировать уже вычисленные площади фигур. За правильность вычисления своих площадей отвечают сами фигуры, а не класс потребитель AreaManager.

Стоит отметить, что если бы мы реализовали Shape как абстрактный класс, а не интерфейс, то сути бы не поменялось. Каждая фигура бы наследовалась от абстрактного класса и переопределяла бы его метод getArea().

Обсуждая Single Responsibility Principle в предыдущем разделе, мы обращались за примером к приложению X-Keeper, где при выборе объекта путём нажатия на чекбокс, он отображается на карте. В приложении таким образом можно выбрать: объект, группу, а также все объекты сразу, если нажать на самый верхний чекбокс. Если взглянуть на это абстрактно, можно заметить, что у нас есть только одна ключевая функция - выбор объекта. Выбор группы или выбор всех объектов - это лишь выполнение ключевой функции необходимое количество раз. Отсюда следует, внутри тела функции checkGroup() мы должны в цикле вызывать функцию checkObject(someObjectId: ...).

Такой подход кажется чистым, правильным, понятным. Он полностью следует ещё одному принципу чистого кода Don't Repeat Yourself, рассмотренному выше, однако есть нюансы. Как вы можете заметить, при выборе одного объекта может измениться состояние чекбокса у группы, также может изменить и состояние общего чекбокса. Опять же, следуя SRP, внутри checkGroup() мы можем иметь функцию groupCheckboxStatusCouldBeChanged(), которая проверяет изменилось ли состояние чекбокса группы, а также функцию generalCheckboxStatusCouldBeChanged(), которая проверяет изменилось ли состояние общего чекбокса. Но как быть при выборе группы? При выборе группы ещё до захода в метод checkObject() известно, что состояние чекбокса группы изменится, причём известно и на какое именно. Аналогично и для общего чекбокса. Первым на ум приходит использовать в аргументах метода checkObject(fromCheckGroup: Boolean, fromCheckAll: Boolean) флаги, которые будут говорить с откуда этот метод был вызван. Если, например, checkObject() вызван из checkGroup(), тогда вызывать groupCheckboxStatusCouldBeChanged() не нужно. Предполагается, что нужное состояние для группы было уже проставлено в checkGroup().

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

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

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

В-третьих, используя для fromCheckGroup и fromCheckAll значения по умолчанию в виде false, возможность сделать так, чтобы изначальная логика работы метода checkObject() не изменилась. Добиться этого достаточно нетрудно:

Хочется дополнительно отметить, что исходя из двух рассмотренных выше примеров, можно сделать вывод, что булевские флаги и проверки типа с помощью instanceof нередко могут сигнализировать о нарушении принципа Open-Closed. Можно порекомендовать использовать булевские флаги и проверки типа с помощью instanceof только при необходимости и с осторожностью, чтобы код был проще поддерживаемым, более чистым и читаемым.

Выше были рассмотрены примеры применения Open-Closed Principle при написании приложений для Android. Однако данная раздел был бы неполным, если не окинуть взглядом то, как спроектирована сама операционная система Android, а именно SDK посредством которого разработчик пользуется возможностями ОС Android. Каждый высокоуровневый класс с которым работает программист, унаследован от более абстрактного.

Давайте пойдём снизу. Возмём TextView, это очень простой элемент, который отображает текст. Теперь давайте посмотрим на Button. На первый взгляд, это принципиально другой элемент, предназначенный для других целей, нежели TextView. Однако так ли это на самом деле? Если присмотреться к любой кнопке, то можно заметить, что кнопка - это простой текст на фоне, и не более. Button, как и TextView, может кастомизироваться путём изменения цвета текста, его шрифта, отступов и т.д. К слову, TextView можно добавить фон и установить действии при нажатии на неё, и мы получим ту самую Button. Именно поэтому Button, как и многие другие элементы Android наследуется от View.

Рис.3

В свою очередь, TextView наследуется от класса View, который содержит свойства и методы, которые общие для всех графических элементов, например, onDraw(), onAttachedToWindow() и прочие. Таким образом, можно заключить, что вся система UI-элементов ОС Android идеально следует принципу открытости-закрытости.

В заключении, хочется отметить, что Open-Closed Principle, не кажущийся на первый взгляд сложным, зачастую на практике требует именно умения его применять. Скорее проще почувствовать, что код плохо поддаётся для расширения или закрыт для модификации, чем найти способ, что это исправить.

2. Архитектурные паттерны

2.1 Введение

приложение проектирование адаптер

2.1.1 «Кричащая архитектура»

Понятие архитектуры приложения достаточно абстрактно. Наверное, при упоминании слова «архитектура» первым на ум приходит что-то связанное со строительством, поэтому давайте с этого примера и начнём. Допустим, у нас есть чертёж здания. Благодаря этому чертежу, мы сразу можем определить для чего это здание предназначено: будь это типичный жилой дом, ледовый дворец, библиотека и т.д. И вот как компоненты постройки, изображённые на чертеже, позволяют нам понять что это за здание и для чего оно предназначено, точно так же и архитектура приложения должна нам «кричать» о том, что оно умеет.

Такое предоставляется возможным, если все компоненты приложения сосредоточены вокруг его бизнес-логики: ключевых сущностей и, самое важное, сценариев использования (use cases). Затем вокруг этих use cases оборачивается всё остальное: пользовательские интерфейсы, базы данных, APIs, любые фреймворки и т.д. То есть ни ключевые сущности, ни use cases ничего не знают об вышеперечисленном. Они первичны.

Очевидным преимуществом первичности use cases является тот факт, что все последующие компоненты гарантированно смогут работать с уже имеющимися ключевыми сущностями и сценариями, которые являются базисом (основой) приложения. Такой подход снимает с нас ограничения на любые детали реализации той или иной функциональности. Более того, у нас есть возможность отложить принятие решения о деталях. Возвращаясь к примеру с постройкой, если это ледовый дворец, нам абсолютно не важно из чего он будет построен: будь это кирпич, камень или ещё что-то. Самое важное, чтобы там была ледовая поверхность.

Как уже упоминалось, грамотно спроектированная архитектура позволяет нам откладывать принятия решений об пользовательских интерфейсах, базах данных, APIs и, вообще, любых фреймворках. Данное преимущество особенно актуально в начале разработки программы. При грамотно спроектированной архитектуре приложения в случае замены, например, SQLite на PostgreSQL вам ни в коем случае не придётся что-то изменять в бизнес-логике. Вам вообще ничего не должно быть нужно менять из того, что уже работало. Стоит понимать, что фреймворки - это лишь инструменты, причём инструменты для построения приложения, а не его архитектуры. В некотором контексте исключением может послужить RxJava, в типы данных которой могут быть обёрнуты ключевые сущности на уровнях, как правило, выше бизнес-логики. Такой подход, наоборот, позволяет укрепить архитектуру. Об этом подробнее рассказано в главе 4.2 RxJava. При этом стоит понимать, что ни типы из RxJava, ни сама эта библиотека ни в коем случае не применяются внутри слоя с бизнес логикой. Про слои (уровни) архитектуры будет подробнее рассказано в следующей главе 3.2 Clean Architecture.

Перемещаясь поближе к Android-разработке, а именно к Java Virtual Machine (далее JVM), на лицо ещё одно очень весомое преимущество тотального отделения бизнес логики от всего остального. Дело в том, что бизнес-логика, написанная на языках, которые транслируются в байт код, понятный для JVM, может использоваться в абсолютно любом приложении, работающим под управлением JVM, будь то: Android, OS X, Windows или даже web-приложение.

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

«Кричащая» архитектура или Screaming Architecture описывает лишь одну из целей использования архитектуры - давать чёткое представление о том, что перед нами за проект и какой функциональностью он обладает. Эту идею подробно описал Robert «Uncle Bob» Martin в своей работе «Clean Architecture», в заключении сопровождая запоминающимся диалогом [20]:

- «… Я вижу в вашем проекте что-то вроде бизнес-логики: сущностей (entities) и сценариев использования (use cases). А как же выглядит пользовательский интерфейс, и я не нашёл откуда вы берёте данные: с сервера или базы данных?»

- «Оу, на данном этапе нас эти вещи не беспокоят. Мы всё это решим позже...».

Преимущества «кричащей» архитектуры:

? тестируема;

? независима от фреймворков;

? легко поддерживаемая;

? легко понятная новый программистам;

Выводы:

? бизнес логика - первична и никак и не от чего не зависит (ни от UI, ни от БД или API, ни от каких фреймворков);

? никогда не позволяйте фреймворкам идти впереди вашей архитектуры или нарушать её;

? придерживайтесь принципа разделения ответственностей (separation of concerns principle).

2.1.2 Ответственность архитектуры

приложение проектирование адаптер

Таблица. 1. Требования к архитектуре Android-приложения

Требования к архитектуре Android-приложения

Требование

Описание / пример

1

Построена вокруг бизнес-логики

Бизнес-логика первична и ни от чего не зависит, т.е она вообще ничего не знает о том, что за её пределами.

2

Независима от используемых фреймворков.

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

2.1

Независима от баз данных

Приложению не важно:

база-данных или API,

MySQL или PostgreSQL.

2.2

Независима от пользовательского интерфейса (UI)

UI возможно заменить безболезненно и в любое время. Например, консольное UI можно заменить на web, при этом не затрагивая код остального приложения.

3

Тестируема

Модули (units) могут быть протестированы отдельно. Приложение может быть протестировано без запущенного сервера или подключенной базы данных.

4

Добавление нового функционала не требует изменения уже работающего

-

5

Изменение имеющегося функционала требует минимальных правок

Переход от MySQL к PostgreSQL затрагивает только замену реализации. Абстракция не меняется.

6

Зацепление между модулями минимально

-

7

Высокая связность внутри каждого модуля

-

8

Легко расширяема

Добавление новый функций в приложение не составляет большого труда и в большинстве случаев выполняется по шаблону (однотипно)

11

Все компоненты приложения могут быть протестированы отдельно

-

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

2.2 The Clean Architecture

Выше был рассмотрен архитектурный подход, в основе которого лежит отделение бизнес-логики приложения от всего остального. Данный подход является базируется на более известном в программной инженерии принципе «разделение ответственности» (separation of concern principle). Это же разделение ответственностей лежит в основе The Clean Architecture - архитектурного паттерна, предложенного всё тем же Robert «Uncle Bob» Martin в его работе «The Clean Architecture» (чистая архитектура) [21]. По сути, работа «Screaming Architecture» легла в основу вышедшей годом позже «The Clean Architecture».

Рис. 4. The Clean Architecture diagram

В чистой архитектуре разделение на слои выражено более чётко, где самым низшим и самым главным является слой бизнес-логики. Однако в The Clean Architecture автором предлагается поделить бизнес-логику на Entities (сущности) и Use Cases (сценарии использования). И тут немножечко остановимся.

Чтобы в процессе чтения данного раздела не возникло мыслей, что данный паттерн не совсем подходит для разработки типичного Android-приложения, необходимо сделать следующее замечание. Паттерн чистая архитектура был предложен Робертом для, так называемого, enterprise ПО. В данном случае enterprise ПО можно понимать как совокупность программ для различных ОС и платформ, причём выполняющих одни и те же функции. Таким образом, Android-приложение - это лишь часть enterpeise ПО. Поэтому применение данного паттерна в качестве архитектуры типичного Android-приложения может показаться оверинжинирингом и это, безусловно, факт. Однако в процессе ознакомления со всем разделом 4. Архитектурные паттерны, станет очевидно, что чистая архитектура, предложенная Мартином универсальна и, с какой-то стороны, более упорядочена в отличие от MV*-паттернов. Из чего следует, что прежде чем рассматривать производные архитектурные паттерны, нужно хорошо понимать родительский.

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

Сущность (entity) может представлять из себя как простой объект (POJO), или набор структур и функций, так и вообще что угодно - то, что будет являться общим между всеми приложениями, входящими в enterprise. Это ключевой момент. Entities во всём Enterprise - это одно и то же. Entities, как правило, должны быть легко представлены в форме, понятной и тому, кто не является программистом. Однако в контексте Android-разработки, зачастую, entity - это основная сущность приложения, POJO или «бизнес-объект», как указано в первоисточнике [21]. Например, если наше приложение - это такси, то поездка - это одна из сущностей. Важно понимать, ничто ни в конкретном приложении, ни во всей экосистеме не может заставить поменять вас Entities. Иначе это бы было другое приложение.

Use cases - это бизнес-правила уже какого-то конкретного приложения в системе. Они инкапсулируют и реализуют сценарии использования всей системы. При этом в своей работе Роберт заявляет, что use cases «оркестрируют» (руководят, управляют) потоком данных как из entities, так и в них. Это очень тонкий момент. Из этого предложения получается, что слой Use Cases несколько посредственный. Он, грубо говоря, приводит в действие выполнение логики внутри Entities.

Как видно на рис. 3, за сценариями использования следует уже группа из Controllers, Gateways и Presenters. Этой группе автором дано название Interface Adapters. Имеющиеся там Controllers нам не особо интересны; они имеют место, скорее, при разработке достаточно больших приложений и в Android-разработке встречаются довольно редко. Их основная задача руководить презентерами (Presenters), если последних слишком, например. Gateways - это уже знакомые нам Repositories - «единственные источники правды».

Порядок появление слоёв - не случайность. Тут вступает в игру так называемое the dependency rule (правило зависимостей) - третий кит, на котором стоит The Clean Architecture Pattern. Никакие изменения последующих слоёв никогда не спровоцируют изменений в предыдущих.

Итак, The Clean Architecture «стоит на трёх китах»:

? separation of concerns;

? разделение на слои;

? the dependency rule.

Можно ещё отметить, что архитектурный паттерн VIPER, который популярен в разработке под iOS достаточно схож с паттерном The Clean Architecture, предложенным Робертом.

Также заметим, что View не должна содержать никакой логики, поэтому существовать Unit-тестов для View не должно [25].

3. Презентационные паттерны

3.1 Введение

приложение проектирование адаптер

В отличие от чистой архитектуры, рассмотренной выше, презентационные паттерны имеют куда большее значения в типичных Android-приложениях. В основе любого из позже рассмотренных презентационных паттернов лежит всё тот же принцип разделения ответственностей. Наиболее чётко видно именно отделение View от всего остального. Реализуется это посредством трёх составляющих, две из которых для всех презентационных паттерном одинаковы - это Model и View, а реализация третьей различается. Именно поэтому презентационные паттерны часто называют MV*-паттернами.

Давайте нумеровать составляющие, начиная от той, которая ближе к пользователю, которой всегда является View. View - это непосредственно сам пользовательский интерфейс, который в Android представлен в виде связки xml-лэйаута и активити или фрагмента. Единственная ответственность View -- это отображать данные пользователю и собирать информацию об отклике. Важно, что View не должна сама непосредственно решать как именно отвечать на отклик пользователя. Это решение - есть бизнес UI-логика, за которую ответственен специальный слой. В конечном итоге View, конечно же, даст ответ на действие пользователя, но сделает это по команде слоя, отвечающего за бизнес-логику. Таким образом, View получается максимально «глупой» - она только отображает данные и делегирует действия пользователя (нажатия, свайпы и т.д) в специальный слой.

Следующая составляющая - это слой бизнес UI-логики. Именно его реализация будет больше всего отличаться в рассмотренных ниже презентационных паттернах. Ответственность данного слоя - принять событие о действии пользователя, которое данном слою делегировала View, и дать на него ответ посредством этой же View. Соответственно, сам ответ представляет из себя готовые данных, которые View должна отобразить. Таким образом, слой бизнес-логики UI управляет View, говоря ей о том, что именно нужно отобразить. Если в ответ на действие пользователя необходимо предоставить данные, например, из базы или с сервера, то данный слой обращается за ними к слою Model. Во время обращения во View данным слоем может быть отправлена команда отобразить прогресс бар, что дать пользователю мгновенный отклик.

Третьим компонентом любого MV*-паттерна является Model. Model - это бизнес-логика. В Model содержится всё то, что связано с тем, где и как получать данные. Иногда в Model располагают всё-то, что не относится ни к пользовательском интерфейсу, ни к логики управления им. Это, скорее, не совсем верный подход, который несколько противоречит «кричащей архитектуре», рассмотренной в главе 3.1.1.

Model у любого MV* паттерна очень часто используется как репозиторий. Это актуально для маленьких приложений, когда репозиторий действительно в состоянии инкапсулировать в себе бизнес-логику, не нарушая Single Responsibility Principle.

Итак, что нам даёт применение презентационного паттерна? Во-первых, независимость UI как от бизнес-логики приложения, так и от бизнес-логики UI. Что, в свою очередь, позволяет нам безболезненно как редактировать, так и полностью изменять пользовательский интерфейс. Во-вторых, уместно будет не перечислять всех преимуществ, а отметить, что такой подход полностью соответствует всем требованиям, которые мы предъявляли в табл. 1. Соответственно, все преимущества от использования презентационного паттерна могут быть найдены в разделе 3.1.2 Ответственность архитектуры.

3.2 MVC

3.2.1 Общие сведения

Первое упоминание об Model-View-Controller появилось в 1970-ых гг. (1979) от Trygve Reenskaug. Вероятно, это был вообще первый как-то названный архитектурный паттерн. Идея этого паттерна была очень простой: Controller собирает введённую пользователем информацию, View - выводит на экран ответ программы (на действие пользователя), а Model - содержит (инкапсулирует) бизнес-логику. Отношение между View и Model можно представить в виде паттерна Observer. Т.е. View observes Model.

Рис. 5. Основная идея MVC

Изначально MVC применялось к каждому элементу на экране. Сегодня мы под View понимаем уже сам экран: Fragment, Activity. [25].

MVC представляет собой реализацию канонического презентационного паттерна, описанного предыдущем разделе. Таким образом, всё, что описано во введении в презентационные паттерны полностью учетно при реализации MVC.

Cо временем, особенно, в контексте Android-разработки Controller из MVC несколько изменился, взяв на себя больше ответственности, что достаточно отличается от оригинальной идеи. Такой подход уже ближе к Model-View-Presenter.

Рис. 6 Модифицированный MVC flow

3.2.2 Преимущества

1. Главным преимуществом MVC можно считать то, что абстрагированность View возведена в абсолют. Т.е. пользовательский интерфейс приложения может быть изменён на кардинально другой без необходимости затрагивать Controller и Model.

3.2.3 Недостатки

1. View имеет ссылку как на Controller, так и на Model [24].

2. В случае первоначальной идеи MVC неочевидно кто должен содержать UI-логику. Рассмотрим следующий пример: нам нужно отобразить фамилию и имя пользователя через запятую (пример: Иванов, Иван). У нас есть model, которая вернёт нам сущность пользователя. У этой сущности есть необходимые методы: getFirstName() и getLastName(). Таким образом, мы можем внутри View получить следующий код:

String firstName = userModel.getFirstName();

String lastName = userModel.getLastName();

nameTextView.setText(lastName + ", " + firstName);

Тогда, к сожалению, Unit-тестирование этой UI-логики невозможно. При этом, если её поместить в Model - тогда получится косвенная зависимость Model от View [23]. В Controller мы не можем переместить UI-логику, потому что в его ответственность входит управлять Model, а не View. Отметим, что в MVP UI-логика уже чётко отнесена к презентеру.

3.3 MVP

3.3.1 Общие сведения

Рис. 7. MVP

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

Наверное, первое, что приходит на ум при упоминании MVP, это обилие интерфейсов. Они тут везде и по многу. Их основная суть здесь - это повышение уровня абстракции, придание большей гибкости и унификации архитектуре. И свою функцию они выполняют прекрасно, но, конечно же, без побочных эффектов не обошлось. Основной побочный эффект здесь - это огромное количество boilerplate кода, однако весь этот код замечательно выносится в base-проект, с наследования которого начинается разработка любого нового приложения.

Овладев паттерном MVP один раз, вы получаете мощный инструмент для построения всех приложений в будущем. Всё дело в том, что последующие приложения будут иметь чёткий скелет, на который будет «насаживаться» необходимый функционал. Грамотное, выполненное с некоторой долей максимализма, разделение ответственностей и позволяет ускорять разработку приложений, что делает этот паттерн одним из самых востребованных у Android-разработчиков. Это находит своё подтверждение при обращении к платформе для поиска работы HeadHunter, где в большинстве вакансий так или иначе требуется знание данного паттерна [30].

Стоит отметить, что ускорение разработки достигается именно за счёт того, что разработка более и менее похожих приложений с применением MVP выглядит практически идентичной. Вы во View-интерфейсе описываете какие данные эта View умеет отображать. В Presenter-интерфейсе описываете, что из себя представляете бизнес-логика UI. Ну, а Model, как и везде инкапсулирует бизнес-логику самого приложения.

Универсальность - безусловно, синоним данного паттерна.

Связь между презентером и соответствующей ему View определена в специальном интерфейсе под названием Contract. Такой подход делает код более читабельным, а связь между View и презентером более понятной [24].

Все презентеры должны реализовывать следующий интерфейс:

interface BasePresenter {

fun subscribe();

fun unsubscribe();

}

или выполненный при помощи дженериков:

interface BasePresenter<T> {

fun takeView(view: T)

fun dropView()

}

3.3.2 Преимущества

1. Унификация разработки приложения, что заметно увеличивает скорость их разработки;

2. Высочайший уровень абстракции. В качестве примера можно взглянуть красивую на реализацию внедрения общего обработчика сетевых ошибок сразу для всего приложения [32].

3.3.3 Недостатки

1. Для небольших приложений огромное количество интерфейсов для реализации этого паттерна может казаться излишним, поэтому зачастую в таких случаях отбрасываются Contract-интерфейсы и/или интерфейс для презентера [24].

2. Класс Presenter может содержать огромное количество строчек кода [24].

3.4 MVVM

3.4.1 Общие сведения

Если MVVM сравнивать с MVP и MVC, то MVVM будет являться более современной, улучшенной версией MVC. А именно: ViewModel представляет из себя усовершенствованный Controller.

Ответственность ViewModel - принимать события о действиях пользователя и испускать потоки данных. Испускание потоков данных - это ключевая, прогрессивная особенность ViewModel. В отличие от MVP, где связь презентера и вью 1:1, ViewModel ничего не знает про View, тем самым одна ViewModel может испускать потоки для скольки угодно вью, а одна вью может слушать данные от скольких угодно ViewModel. Примечательной особенностью является тот факт, что ViewModel всё также управляет View, как и другие паттерны, рассмотренные выше, но при этом ViewModel совершенно ничего не знает про то, какой именно View она управляет. Это даёт совершеннейшую гибкость и огромной простор для использования ViewModel.

Например, за счёт такого простора на одной лишь ViewModel можно построить всю систему навигации в Android-приложении. Автор данной работы самостоятельно придумал и реализовал эту идею, а также протестировал её в нескольких проектах с single Activity. Идея следующая: есть NavigationViewModel, которая работает на уровне активити, все фрагменты, с которых можно продвинуться по приложению на следующий экран, имеют ссылку на эту NavigationViewModel. Событие, после которого должен осуществиться переход на другой экран, должно быть передано NavigationViewModel. И, в свою очередь, активити на основе испускаемых NavigationViewModel потоков, переключает фрагменты. Аналогово данному подходу на момент написания данной работы обнаружено не было.

Схематично принцип работы Model-View-Model можно продемонстрировать следующим образом:

Рис. 8. Устройство MVVM

В отличие от MVP абстракция здесь достигается не с помощью интерфейсов, поэтому применять данный паттерн можно полностью без них. При этом dependency rule, описанное в начале этой главы, здесь также отлично выполняется. Мы без проблем сможем заменить пользовательский интерфейс (View) или базу данных, не меняя при этом в приложении ничего, кроме тех компонентов, которые мы хотим заменить непосредственно. Что касается View, она находится на самом высоком уровне и просто поглощает данные и передаёт события во ViewModel, поэтому её замена никак не связана ни с бизнес-логикой пользовательского интерфейса, ни с логикой работы приложения, подавно. Если мы захотим изменить View, мы просто удалим старую и будет поглощать потоки с помощью новой, не забывая передавать пользовательские события во ViewModel. Замена фреймворка по работе с базой данных тоже никак не повлияет на работу приложения, поскольку Model работает с абстракциями источников данных, а не с их реализациями. Такой подход сохранён в любом MV*-презентационном паттерне.

...

Подобные документы

  • Архитектура и история создания операционной системы 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

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.