Android studio builder это

Эволюция паттерна Builder в Android.

Продолжаем тему паттернов проектирования. На этот раз рассмотрим порождающий шаблон проектирования Builder. Мы рассмотрим какую проблему решает Builder в Java, какие у него есть “братья” и как этот паттерн эволюционировал в Android-разработке при использовании Kotlin.

Проблема

В первую очередь хочется поговорить, о проблеме, которую призван решить Builder. Чаще всего Строитель (он же Builder) используется для пошаговой инициализации множества полей и вложенных объектов. Код инициализации таких объектов чаще всего спрятан внутри монструозного конструктора с десятком параметров. Либо ещё хуже — распылён по всему клиентскому коду. Перед тем как перейти непосредственно к самому паттерну — давайте рассмотрим его “младших братьев” которые могут теоретически помочь при решении описанной выше проблемы.

Так себе решение #1. Телескопический конструктор

Традиционно, программисты использовали Telescoping Constructor паттерн. Суть этого паттерна состоит в том, что Вы предоставляете несколько конструкторов: конструктор с обязательными параметрами, конструктор с одним дополнительным параметром, конструктор с двумя дополнительными параметрами, и так далее. Продемонстрируем как это будет выглядеть на практике. Для краткости будем использовать только 4 дополнительных параметра.

Таким образом, когда Вы хотите создать объект данного класса, Вы используете конструктор с необходимым списком параметров:

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

Минусы такого подхода:

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

Нужно тщательно высчитывать позицию параметра, чтобы выяснить к какому полю он относится — иначе можно легко перепутать, что станет багом, который сложно будет найти

Так себе решение #2. JavaBeans паттерн

Видели множество set()-методов для создания сложного объекта? Думаю все видели размазанные по всему коду сетеры — которые оставляют неконсистентное состояние при инициализации объекта:

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

“The JavaBeans pattern has serious disadvantages.” — Joshua Bloch, Effective Java

Минусы такого подхода:

Поскольку строительство разделено между несколькими вызовами, JavaBean может находиться в неустойчивом состоянии частично пройдя через конструирование. Попытка использования объекта, если он находится в неустойчивом состоянии может привести к ошибкам, которые далеки от кода, содержащего ошибку, следовательно, трудными для отладки. Также JavaBeans паттерн исключает возможность сделать класс неизменным(immutable), что требует дополнительных усилий со стороны программиста для обеспечения безопасности в многопоточной среде.

Хорошее решение #3. Паттерн Builder

Третья альтернатива, сочетающая в себе безопасность паттерна Telescoping Constructor с читаемостью паттерна JavaBeans — это паттерн Builder.

Вместо непосредственного создания желаемого объекта, клиент вызывает конструктор (или статическую фабрику) со всеми необходимыми параметрами и получает объект строителя. Затем клиент вызывает сеттер-подобные методы у объекта строителя для установки каждого дополнительного параметра. Наконец, клиент вызывает метод build() для генерации объекта, который будет являться неизменным(immutable). Строитель является статическим внутренним классом в классе, который он строит. Вот как это выглядит на практике:

Ну и теперь конструирование объекта сводится к вызову необходимых методов:

Преимущества паттерна:

  • Позволяет создавать продукты пошагово.
  • Позволяет использовать один и тот же код для создания различных продуктов.
  • Изолирует сложный код сборки продукта от его основной бизнес-логики.

Хорошее решение #4. Паттерн Builder в Kotlin

На дворе 2021 год, и программирование под Android развивается семимильными шагами. И если раньше подход №3 считался стандартом, то встаёт вопрос: “А надо ли реализовывать паттерн Builder в Kotlin?” Перед тем, как ответить на данный вопрос, давайте вспомним цели, которые мы хотели достичь, используя Builder:

  • Конструирование сложного объекта с большим количеством аргументов (возможно с одинаковым типом)
  • Возможность создания разных объектов с разным количеством аргументов (часть из аргументов может быть опциональными)
  • Неизменяемость построенного объекта

Теперь, давайте пройдёмся по каждому пункту и посмотрим, что нам может предложить Kotlin.

В Koltin есть 2 крутые фичи: именованныt аргументы и дефолтные аргументы. Таким образом, мы убиваем 2-ух зайцев сразу. Во-первых, используя именнованные аргументы, мы теперь можем передавать в конструктор 100500 различных аргументов и при этом они могут быть одного типа — перепутать их будет достаточно сложно. А использование дефолтных значений для аргументов — избавляет нас постоянно описывать ненужные значения — как это было в подходе с телескопическим конструктором. Ну а неизменяемость построенного объекта очень легко достичь, объявив поля класса через val.

Читайте также:  Установить indriver для андроид

Сконструировать такой объект теперь мы можем так:

Таким образом, Kotlin очень сильно упростил нам задачу. И теперь паттерн Builder является частью языка, и нет необходимости конвертировать его Java-реализацию. Достаточно придерживаться описанных выше правил.

Надеюсь, в этой статье вы уловили суть паттерна Builder, а также теперь знаете врагов в лице парочки антипаттернов и сможете писать более читабельный и эффективный код на Kotlin. Ну а если вы хотите прокачаться в Android-разработке, то приглашаю вас на 3-ий поток интенсива по Android-разработке на Kotlin. До 25 января скидка 15%. Узнать подробности можно по ссылке.

Подписывайся на канал чтобы не пропустить анонс и хлопай внизу если статья был полезная.

Понравилась статья? Не забудь подписаться и поставить лайк, а ещё

Источник

Java. Шаблон Builder

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

Если ваш класс содержит большое количество конструкторов и статических методов, то возникает проблема — трудно запомнить порядок и назначение необязательных параметров при вызове класса. В таких случаях рекомендуется использовать шаблон Builder.

Создадим для примера «плохой» класс с двумя обязательными и с четырьмя необязательными параметрами.

Создание экземпляра класса с использованием конструктора со всем набором параметров.

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

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

Количество кода в классе увеличивается, но код стал более читаемым при создании экземпляра класса.

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

Поэтому был придуман третий вариант, который назвали шаблоном Builder. Вместо непосредственного создания объекта класса вызывается конструктор (или статический метод) со всеми необходимыми параметрами, чтобы получить объект Builder. Затем вызываются сеттеры для установки всех необходимых параметров. В завершение, вызывается метод build() для генерации объекта, который будет являться неизменным. Реализуется такой подход через статический внутренний класс.

Создадим уже «хороший» класс с применением Builder.

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

При таком подходе код проще писать и легко читать. У шаблона есть и другие преимущества, например, можно задействовать несколько параметров varags.

Недостатком шаблона является затраты на создание класса Builder, но если вы будете позже расширять класс, то подход оправдает себя.

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

Источник

Элегантный Builder на Java

Наверняка большинство сколько-нибудь опытных программистов знакомы с паттерном Builder. Он позволяет сделать инициализацию структур данных более наглядной, гибкой при этом сохраняя такое полезное их свойство как неизменяемость (immutability). Вот классический пример с первой страницы выдачи гугла на запрос «java builder pattern example». При всех своих преимуществах, самый главный недостаток данной реализации паттерна — в два раза больше кода, по сравнению с обычным плоским бином. Если генерация этого дополнительного кода не проблема для любой популярной IDE, то редактировать такой класс становится достаточно утомительно и читабельность страдает в любом случае.

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

Итак, у нас есть неизменяемая структура

Здесь сейчас всего два поля, но билдер все равно был бы полезен чтобы не путать порядок параметров в конструкторе и если понадобится проинициализировать только одно поле из двух или оба, но в разные моменты времени. Что говорить, когда полей станет 20!

Чтобы не дублировать поля в классе-билдере, просто заводим внутренний класс. Он имеет доступ к приватным полям своего родительского класса и может выставлять их напрямую. Собственный конструктор класса сделаем приватным, а с полей уберем модификатор final.

Конструктор у билдера тоже приватный, иначе имея доступ к экземляру Account можно было бы сделать билдер и через него изменять поля уже созданного объекта. Метод build просто возвращает уже готовый объект (здесь можно проверять все ли обязательные поля на месте, например.

Последний штрих — добавляем в метод для создания экземпляра билдера.

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

Попробуйте добавить новое поле или изменить тип поля token в том и другом случае. С увеличением количества полей разница в объеме кода и читабельности будет все более заметной. Сравним пример из статьи, на которую я ссылался в начале топика (я его изменил, чтобы стили примеров совпадали):

И реализация через внутренний класс:

Читайте также:  Во что сыграть андроид

Можно заметить, что с точки зрения организации кода такой класс отличается от обычного плоского бина с полями и гетерами-сетерами только тем, что сеттеры сгруппированы в отдельном внутреннем классе, добавились только пара методов newBuilder() и build(), строчка с объявлением внутреннего класса и приватные конструкторы.

1. Метод build билдера возвращает один и тот же объект и если после его вызова продолжить выставлять поля через методы билдера, поля уже созданного объекта будут меняться. Это легко исправить, если создавать каждый раз новый экземпляр объекта:

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

2. Спасибо комментаторам, мои сомнения развеяны — объект, получаемый из такого билдера, не потокобезопасен из-за того, что поля в нем не объявлены как final. Если в вашем приложении важен этот момент, лучше использовать классический билдер.

И напоследок — использование билдера.

Опять же Account.newBuilder() моим программерским глазам милее, чем new Account.Builder(), хотя это уже дело вкуса.

Всем чистого кода!

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

Источник

Диалоговые окна

В Android 3.0 (API 11) появилась новинка — класс android.app.DialogFragment и его аналог android.support.v4.app.DialogFragment, а чуть позже и android.support.v7.app.AppCompatDialogFragment из библиотеки совместимости, позволяющие выводить диалоговое окно поверх своей активности. Раньше использовался класс Dialog и его производные, например, AlertDialog. Они никуда не делись, только теперь их нужно встраивать в фрагмент, который выступает в качестве контейнера. Поэтому условно разговор о диалоговых окнах можно разбить на две части — как использовать DialogFragment и как пользоваться классами Dialog, AlertDialog и другими диалоговыми окнами. После появления AndroidX имена пакетов очередной раз изменились, теперь следует импортировать androidx.fragment.app.DialogFragment, androidx.appcompat.app.AlertDialog и другие аналогичные классы.

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

Для создания диалога следует наследоваться от класса DialogFragment. Создадим новый класс MyDialogFragment:

Допустим у нас есть кнопка на экране активности. Вызвать диалоговое окно можно через метод show().

Скорее всего вы увидите пустой прямоугольник или квадрат. А возможно у вас просто потемнеет экран активности.

Так как это обычный фрагмент, то нам нужно позвать менеджера фрагментов и попросить его показать фрагмент.

Для вызова диалога мы создаём экземпляр класса MyDialogFragment и вызываем метод show(). Метод принимает два параметра: объект класса FragmentManager, получаемый через метод getSupportFragmentManager(), и тег — идентификатор диалога в виде строковой константы, по которому можно идентифицировать диалоговое окно, если их будет много в нашем проекте.

Существует и альтернативный вариант показа окна через транзакцию.

Мы получили пустой бесполезный фрагмент. Следует заняться его конструированием. В созданном классе нужно переопределить метод onCreateDialog(). Если используется разметка, то также используется метод onCreateView(), как и у обычных фрагментов. Скорее всего вы не будете заново изобретать велосипед, а будете использовать готовые варианты диалоговых окон.

AlertDialog

Самый распространённый вариант диалогового окна — это AlertDialog. С него и начнём.

Диалоговое окно AlertDialog является расширением класса Dialog, и это наиболее используемое диалоговое окно в практике программиста. Очень часто требуется показать диалог с кнопками Да и Нет, а также Мур и Мяу . В создаваемых диалоговых окнах можно задавать следующие элементы:

  • заголовок
  • текстовое сообщение
  • кнопки: от одной до трёх
  • список
  • флажки
  • переключатели

AlertDialog с одной кнопкой

Начнём с простого примера — покажем на экране диалоговое окно с одной кнопкой.

В класс фрагмента добавляем метод.

Внешний вид от версии к версии может меняться. В частности, недавно поменяли цвет текста для кнопки.

Сначала мы создаём объект класса AlertDialog.Builder, передав в качестве параметра ссылку на активность. Затем, используя методы класса Builder, задаём для создаваемого диалога заголовок (метод setTitle()), текстовое сообщение в теле диалога (метод setMessage()), значок (метод setIcon()), а также кнопку через метод под странным названием setPositiveButton().

Сама обработка нажатия кнопки внутри диалогового окна задаётся внутри метода setPositiveButton(). В нашем случае мы просто закрываем окно диалога через метод cancel().

Обратите внимание на не совсем обычный способ вызова череды методов цепочкой через точку .setMessage(«Покормите кота!»).setIcon(R.drawable.ic_android_cat) и т.д. Такой синтаксис можно часто увидеть в jQuery. При таком способе не нужно использовать точку с запятой в конце каждого метода, вы просто склеиваете все вызовы. Но можете использовать и обычный синтаксис.

Если вы используете Java 8, то студия предложит использовать лямбда-выражение вместо анонимного класса. Решайте сами, в каком стиле писать код.

Заголовок. Метод setTitle()

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

А если очень хочется вывести длинную строку в заголовке? Тогда вместо setTitle() можно вызвать setCustomTitle() и передать ему View, в нашем случае это будет TextView.

При этом перестал выводиться значок. Обидно.

Нелёгкий выбор — пример с двумя кнопками

Теперь рассмотрим пример создания диалогового окна с двумя кнопками на основе иллюстрации.

Читайте также:  Android vnc server без root

Внешний вид диалоговых окон в разных версиях Android.

Общая часть кода осталась прежней — объект класса AlertDialog.Builder, методы для настройки окна, а также кнопки диалога и обработку событий на них. В AlertDialog можно добавить только по одной кнопке каждого типа: Positive, Neutral и Negative, т. е. максимально возможное количество кнопок в диалоге — три. На названия кнопок не обращайте внимания, они не несут смысловой нагрузки, а только определяют порядок вывода. Причём в разных версиях Android порядок менялся. Поэтому на старых устройствах кнопка «Да» может быть первой, а на новых — последней. Для каждой кнопки используется один из методов с префиксом set. Button, которые принимают в качестве параметров надпись для кнопки и интерфейс DialogInterface.OnClickListener, определяющий действие при нажатии. Чтобы пользователь не мог закрыть диалог нажатием в любой точке экрана, вызывается метод setCancelable() с значением true.

Три кнопки

Рассмотрим пример с тремя кнопками. Разницы практически нет. Повторяем все предыдущие шаги, для отображения диалогового окна вызывается метод builder.create(). Например, для создания диалога с кнопками Мяу, Гав, Сам дурак! код будет выглядеть приблизительно так:

AlertDialog со списком

Если вам нужно диалоговое окно со списком выбираемых пунктов вместо кнопок, то используйте метод setItems(), где нужно указать массив данных для отображения в списке диалога. Данный метод нельзя использовать вместе с методом setMessage(), так они выводят содержимое в основной части окна.

Выбранный элемент содержится в параметре which. При выборе одного из пунктов меню появится всплывающее уведомление, показывающее выбранного кота.

Напоминаю, что внешний вид окна менялся от версии к версии.

AlertDialog с переключателями

Для создания диалогового окна с переключателями применяется метод setSingleChoiceitems() вместо метода setItems().

Обратите внимание на следующие детали. При выборе переключателя диалоговое окно закрываться не будет. Поэтому необходимо предусмотреть механизм закрытия окна, например, добавить кнопку. Второй момент — в методе setSingleChoiceitems для первого параметра используется массив значений для переключателей, а для второго параметра используется целочисленное значение индекса переключателя, который будет включён по умолчанию при вызове диалогового окна. Если вы хотите, чтобы все переключатели при запуске были в выключенном состоянии, то используйте значение -1.

AlertDialog с флажками

Если вы хотите использовать вместо переключателей флажки (CheckBox) для множественного выбора, то вам нужен метод setMultiChoiceItems(). Код практически идентичен предыдущему примеру:

Первый параметр в методе setMultiChoiceItems() — массив значений для списка с флажками, второй параметр — булевый массив состояний флажков списка по умолчанию при вызове диалога. Например, мы хотим, чтобы второй элемент списка был отмечен флажком, а остальные элементы нужно оставить неотмеченными. В этом случае используем массив из булевых значений:

Как и в предыдущем случае с переключателями, для диалогового окна с флажками необходимо использовать кнопки для закрытия окна.

Автоматическое закрытие окна

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

Добавим в проект кнопку для вызова самозакрывающего диалогового окна и напишем код для обработчика щелчка кнопки (старый пример без использования фрагмента):

AlertDialog с рейтингом (проблемный пример)

Однажды я повстречал в сети пример включения компонента RatingBar в диалог. При тестировании обнаружил, что код работает не совсем корректно. На экран всегда выводятся шесть звёздочек, несмотря на установленные настройки. А если развернуть приложение в альбомный режим, то выводятся и семь звёздочек и больше, в зависимости от размеров экрана. Оставил пример на память, если кто-то разберётся в чём тут была проблема, то дайте знать. Это тоже старый пример без использования фрагмента.

Решение проблемы

Читатели предложили несколько вариантов решения проблемы. Как следует из документации, компонент RatingBar следует размещать в макете, который имеет свойство wrap_content. Поступим следующим образом. Создадим отдельную разметку с RatingBar, которую присоединим к диалоговому окну. Итак, разметка:

res/layout/ratingdialog.xml

Теперь модифицированный код:

Обратите внимание, что для доступа к свойствам RatingBar, мы вызываем метод View.findViewById(), а не Activity.findViewById(), как обычно мы привыкли делать в методе onCreate(), когда опускаем название класса.

Передать данные в активность

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

В коде каких-то сложностей нет — устанавливаем заголовок, значок, кнопки. При построении диалогового окна указываем родительскую активность и название методов в ней, которые будут отвечать за обработку нажатий кнопок диалога — в нашем случае это методы okClicked() и cancelClicked(). Кстати, имена методов будут подчёркнуты красной линией и среда разработки предложит создать данные методы в классе активности (используйте комбинацию клавиш Alt+Enter).

Возвращаемся в код главной активности и пропишем код для нажатий кнопок диалогового окна:

Пример был написан по принципу — «работает и ладно». На самом деле пример не совсем грамотный, хотя даже в документации он ещё встречается.

Правильный вариант рассматривается во второй части о диалоговых окнах DialogFragment.

AlertDialog с собственной разметкой

Если стандартный вид AlertDialog вас не устраивает, то можете придумать свою разметку и подключить её через метод setView()

Вы познакомились с базовыми принципами использования диалоговых окон.

Источник

Оцените статью