[Совершенный код, Разработка мобильных приложений, Разработка под Android, Kotlin] Kotlin Best Practices
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Kotlin, созданный всего 5 лет назад, с 2019 года считается приоритетным языком программирования под Android. И все же этот язык достаточно молод и продолжает развиваться, поэтому иногда бывает непонятно, каким образом лучше написать код. У нас в команде часто бывают обсуждения на тему чистого Kotlin-кода, и на их основе мы составили свои best practices. Хотим поделиться этими рекомендациями и ждем ваших вопросов.Ну что ж, приступим! В первую очередь, в Котлине много синтаксического сахара, и если им злоупотреблять, то читать такой код становится затруднительно. Следующие несколько пунктов можно отнести к борьбе между краткостью и читаемостью.
Не пишите объявление класса в одну строчкуДаже если сегодня объявление вашего класса уместилось в одну строчку и не выходит за поля – завтра может добавиться еще один аргумент в конструктор или еще один интерфейс в список наследуемых типов. В таком случае этот самый список может уехать вправо и пропасть из поля видимости.
class ChannelViewModel(
val conversationId: String,
getChannelUseCase: GetChannelUseCase,
) : ViewModel() {
Размещая каждый параметр конструктора на отдельной строке, мы также получаем бонусы:
- Если при рефакторинге надо поменять местами параметры, сделать это будет быстрее (в Android Studio сдвигаем строчку кода вверх/вниз с помощью сочетания клавиш Shift+Command+Up/Down)
- В гите изменения параметров будут отображаться более наглядно.
Вложенный returnРассмотрим такой пример:
data class MyClass(val number: Int, val flag: Boolean)
fun create(numberParam: Int?, flag: Boolean?): MyClass? {
return MyClass(numberParam ?: return null, flag == true)
}
Обычно если мы доходим до выражения с return, то это такая точка, в которой завершается выполнение функции. В этом же примере в случае, когда numberParam равен null, мы мысленно проходим сперва через return MyClass(...) и затем через return null. Лучше в данном случае использовать простой if, чтобы поток выполнения программы выглядел понятнее:
fun create(numberParam: Int?, flag: Boolean?): MyClass? {
if (numberParam == null) {
return null
}
return MyClass(numberParam, flag == true)
}
Анонимный параметр itКак давно замечали на Хабре, цепочка из вложенных методов с анонимным параметром it читается тяжело, однако такие злоупотребления по-прежнему регулярно встречаются в коде:
values?.filterNot { selectedValues?.contains(it) == true }
?.let {
selectedValues?.addAll(it)
result[key]?.values = selectedValues?.filter { it.isChecked }
}
Именованный параметр следует добавить как минимум в функцию let:
values?.filterNot { allSelectedValues?.contains(it) == true }
?.let { newValues ->
allSelectedValues?.addAll(newValues)
result[key]?.values = allSelectedValues?.filter { it.isChecked }
}
В этом коде it – не единственная проблема. В данном примере обнуляемые типы можно заменить на не-null, в результате код станет опрятнее. Сравните:
val newValues = values.filterNot { selectedValues.contains(it) }
selectedValues.addAll(newValues)
result[key]?.values = selectedValues.filter { it.isChecked }
Сокращайте цепочки безопасных вызовов ?. заменой обнуляемых типов на не-null типыПродолжим затронутую в предыдущем пункте тему с обнуляемыми типами и рассмотрим такой пример:
private var animatedView: FrameLayout? = null
...
animatedView?.animate()?.alpha(1f)?.setDuration(500)?.interpolator = AccelerateInterpolator()
Здесь фактически значение null могло бы появиться только в том случае, когда animatedView равен null. Добавление простой проверки if (animatedView != null) избавит нас от цепочки безопасных вызовов. Но в данном примере вообще нет необходимости в том, чтобы animatedView принимало значение null. Поэтому лучше его сделать lateinit переменной, и тогда код вообще не будет содержать проверок на null:
private lateinit var animatedView: FrameLayout
...
animatedView.animate().alpha(1f).setDuration(500).interpolator = AccelerateInterpolator()
Некоторые проблемы появляются при автоматическом переводе файлов с Java на Kotlin, особенно если разработчик поторопился и оставил код “как есть”. Обычно такой код пестрит восклицательными и вопросительными знаками и требует пересмотра. При переходе с Java какие-то if можно заменить на when, можно использовать функции области видимости (let, apply, also, with, run), функции для работы с коллекциями, переписать классы Utils на extension функции.
Избавляемся от !!Использование оператора !! считается дурным тоном, так как оно означает игнорирование потенциального NullPointerException, да и сам код с обилием знаков !! выглядит “костыльно”. От !! можно избавиться следующими способами:
- заменой обнуляемых типов на не-null типы (как было сделано в примере с lateinit animatedView)
- функцией let с безопасным вызовом ?.let { … }
- элвис-оператором ?:
- и наконец, если мы действительно допускаем, что в приложении должно произойти исключение, когда переменная приняла значение null, то можно воспользоваться функцией checkNotNull или requireNotNull. Они отличаются только типом выбрасываемого исключения: IllegalStateException и IllegalArgumentException соответственно.
Для того, чтобы предупредить появление !! при переводе кода с Java на Kotlin, можно добавить аннотации @NonNull в Java-код.Отдаем предпочтение when перед ifСравните:
val price = if (priceData.isWeightPrice) {
priceData.minDiscountPrice.toInt()
} else if (priceData.discountPrice != 0.0) {
priceData.discountPrice.toInt()
} else {
priceData.price.toInt()
}
и вариант с when:
val price = when {
priceData.isWeightPrice -> priceData.minDiscountPrice.toInt()
priceData.discountPrice != 0.0 -> priceData.discountPrice.toInt()
else -> priceData.price.toInt()
}
С when мы можем не писать лишние круглые и фигурные скобки, но код все равно выглядит структурированным, так как условия отделены от результатов стрелками. Преимущества when заметны в том случае, когда проверяется более двух условий. Конечно, нет смысла использовать when для проверки значений булевского флага.Заменяем классы Util на функции расширенияЕсли статические вспомогательные функции можно отнести к какому-то определенному классу, лучше оформить их в виде функций extension. Тогда код будет выглядеть более идиоматичным. Еще один плюс функций расширения в том, что автокомплит сам их предлагает. Не надо держать в голове, реализована ли уже на проекте та или иная вспомогательная функция, и в каком файле она лежит.Это не значит, что от объектов Util в Kotlin надо полностью избавляться (обратите внимание: статические методы в Java содержатся в классах, а в Kotlin – в объектах). Если вспомогательная функция не относится к какому-то конкретному типу, не стоит оформлять ее в виде статической функции верхнего (package) уровня. Иначе такие функции будут постоянно появляться в автокомплите (extension функцию автокомплит предложит после того, как мы напечатаем название переменной определенного типа и точку, и это нормально; а функция верхнего уровня появится сразу после того, как мы начнем что-то печатать). Кроме того, код будет лучше организован и структурирован, если вспомогательные функции помещать внутри объекта, а не просто внутри файла.
Котлин периодически обновляется, и иногда разработчик может не уследить за отдельными новшествами (и это не говоря о случаях “прочитал и забыл”). Приведем несколько полезных возможностей языка, которые мы советуем использовать.
Замыкающие запятые (trailing commas) Замыкающие запятые появились в версии языка 1.4. Мы рекомендуем их использовать в сочетании с приведенной выше рекомендацией располагать параметры конструктора (и вообще метода) на отдельных строках по той же причине: с замыкающей запятой аккуратнее выглядит diff в гите при добавлении параметра в конец. Single Abstract Method interface (Fun interface)Тоже появились в Kotlin 1.4.0. Сравните, как выглядит создание анонимного объекта для обычного и fun интерфейса:
this.actionClickListener = object : BubbleView.ClickListener {
override fun onBubbleViewClick() {
...
}
}
и
this.actionClickListener = BubbleView.ClickListener {
...
}
Во втором случае к объявлению интерфейса добавилось только fun:
fun interface ClickListener {
fun onBubbleViewClick()
}
Таким образом, вместо одного интерфейса в Java, в Kotlin можно использовать обычный интерфейс, SAM-интерфейс или лямбду. Предлагаем в этом выборе руководствоваться следующим правилом:
- для простых колбеков и мапперов используем функциональный тип (T) -> R;
- если для наглядности мы хотим иметь название для типа или для вызываемого метода, то SAM-интерфейс;
- если должно быть несколько методов, то обычный интерфейс.
Функции для работы с коллекциямиKotlin содержит огромное множество функций для сортировки, фильтрации, поиска, преобразования коллекций. Каждый раз, когда в коде встречается цикл для обхода коллекции, велика вероятность, что его можно переписать с использованием уже существующей функции. Поэтому если вы регулярно пишете циклы, рекомендуем вам изучить/освежить в памяти документацию. Как можно улучшить представленный код?
val itemIdsSet: Set<String> = ...
val currentItemIds: Set<String> = ...
for(itemId in itemIdsSet) {
if(!currentItemIds.contains(itemId)) {
repository.exclude(itemId)
}
}
Как вариант:
val itemIdsSet: Set<String> = ...
val currentItemIds: Set<String> = ...
for (itemId in itemIdsSet subtract currentItemIds) {
repository.exclude(itemId)
}
Или если отредактировать API репозитория так, чтобы функция exclude могла принимать коллекции, тогда мы сможем написать:
repository.exclude(itemIdsSet subtract currentItemIds)
Согласуйте code styleИногда стиль написания кода у разработчиков в команде может настолько различаться, что проект рискует превратиться в “кашу”. Поэтому лучше согласовывать такие моменты:
- принципы наименования переменных (например, запрет на использование префикса _ для теневых полей или написание констант enum только заглавными буквами)
- порядок следования функций внутри классов
- положение companion object внутри класса (в начале или в конце) и т.п.
ЗаключениеВ этой статье мы поделились несколькими правилами, которые показали свою эффективность в нашей команде. Однако, мы не претендуем на их истинность и рекомендуем вам обсуждать и формировать свои наборы best practices.Как пример, больше идей по улучшению Kotlin-кода можно почерпнуть здесь:https://developer.android.com/kotlin/style-guide https://proandroiddev.com/an-opinionated-guide-on-how-to-make-your-kotlin-code-fun-to-read-and-joy-to-work-with-caa3a4036f9e
https://developer.android.com/kotlin/coroutines/coroutines-best-practices
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка под iOS, Разработка мобильных приложений, Разработка под Android] Эволюция социального фида в iFunny — мобильном приложении с UGC-контентом
- [Программирование, Разработка под iOS, Разработка под Android, Тестирование мобильных приложений] Автоматизация тестирования мобильных приложений. Часть 1: проверки, модули и базовые действия
- [Разработка под iOS, Разработка мобильных приложений, Разработка под Android] Как выйти на китайский рынок с mini-app для WeChat, чтобы не прогореть
- [Программирование, Разработка мобильных приложений, Dart, Flutter] Flutter 2: что нового (перевод)
- [Разработка мобильных приложений] Тупые способы сэкономить на мобильной разработке
- [Разработка под Android, Kotlin] Kotlin. Лямбда vs Ссылка на функцию
- [Хостинг, Разработка веб-сайтов, Разработка под Android, API, Транспорт] Как реализовать отслеживание местоположения андроид устройства на своем сайте
- [Open source, Разработка под iOS, Разработка мобильных приложений, Swift] Как мы ускоряли работу отладчика Swift
- [Программирование, Разработка под Android, Kotlin] О взаимосвязи между корутинами, потоками и проблемами параллелизма (перевод)
- [Игры и игровые приставки] Эмулятор PS2 на Android — вторая серия
Теги для поиска: #_sovershennyj_kod (Совершенный код), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_razrabotka_pod_android (Разработка под Android), #_kotlin, #_android, #_kotlin, #_clean_code, #_refactoring, #_blog_kompanii_simbirsoft (
Блог компании SimbirSoft
), #_sovershennyj_kod (
Совершенный код
), #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
), #_razrabotka_pod_android (
Разработка под Android
), #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 23:27
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Kotlin, созданный всего 5 лет назад, с 2019 года считается приоритетным языком программирования под Android. И все же этот язык достаточно молод и продолжает развиваться, поэтому иногда бывает непонятно, каким образом лучше написать код. У нас в команде часто бывают обсуждения на тему чистого Kotlin-кода, и на их основе мы составили свои best practices. Хотим поделиться этими рекомендациями и ждем ваших вопросов.Ну что ж, приступим! В первую очередь, в Котлине много синтаксического сахара, и если им злоупотреблять, то читать такой код становится затруднительно. Следующие несколько пунктов можно отнести к борьбе между краткостью и читаемостью. Не пишите объявление класса в одну строчкуДаже если сегодня объявление вашего класса уместилось в одну строчку и не выходит за поля – завтра может добавиться еще один аргумент в конструктор или еще один интерфейс в список наследуемых типов. В таком случае этот самый список может уехать вправо и пропасть из поля видимости. class ChannelViewModel(
val conversationId: String, getChannelUseCase: GetChannelUseCase, ) : ViewModel() {
data class MyClass(val number: Int, val flag: Boolean)
fun create(numberParam: Int?, flag: Boolean?): MyClass? { return MyClass(numberParam ?: return null, flag == true) } fun create(numberParam: Int?, flag: Boolean?): MyClass? {
if (numberParam == null) { return null } return MyClass(numberParam, flag == true) } values?.filterNot { selectedValues?.contains(it) == true }
?.let { selectedValues?.addAll(it) result[key]?.values = selectedValues?.filter { it.isChecked } } values?.filterNot { allSelectedValues?.contains(it) == true }
?.let { newValues -> allSelectedValues?.addAll(newValues) result[key]?.values = allSelectedValues?.filter { it.isChecked } } val newValues = values.filterNot { selectedValues.contains(it) }
selectedValues.addAll(newValues) result[key]?.values = selectedValues.filter { it.isChecked } private var animatedView: FrameLayout? = null
... animatedView?.animate()?.alpha(1f)?.setDuration(500)?.interpolator = AccelerateInterpolator() private lateinit var animatedView: FrameLayout
... animatedView.animate().alpha(1f).setDuration(500).interpolator = AccelerateInterpolator() Некоторые проблемы появляются при автоматическом переводе файлов с Java на Kotlin, особенно если разработчик поторопился и оставил код “как есть”. Обычно такой код пестрит восклицательными и вопросительными знаками и требует пересмотра. При переходе с Java какие-то if можно заменить на when, можно использовать функции области видимости (let, apply, also, with, run), функции для работы с коллекциями, переписать классы Utils на extension функции.
Избавляемся от !!Использование оператора !! считается дурным тоном, так как оно означает игнорирование потенциального NullPointerException, да и сам код с обилием знаков !! выглядит “костыльно”. От !! можно избавиться следующими способами:
val price = if (priceData.isWeightPrice) {
priceData.minDiscountPrice.toInt() } else if (priceData.discountPrice != 0.0) { priceData.discountPrice.toInt() } else { priceData.price.toInt() } val price = when {
priceData.isWeightPrice -> priceData.minDiscountPrice.toInt() priceData.discountPrice != 0.0 -> priceData.discountPrice.toInt() else -> priceData.price.toInt() } Котлин периодически обновляется, и иногда разработчик может не уследить за отдельными новшествами (и это не говоря о случаях “прочитал и забыл”). Приведем несколько полезных возможностей языка, которые мы советуем использовать.
Замыкающие запятые (trailing commas) Замыкающие запятые появились в версии языка 1.4. Мы рекомендуем их использовать в сочетании с приведенной выше рекомендацией располагать параметры конструктора (и вообще метода) на отдельных строках по той же причине: с замыкающей запятой аккуратнее выглядит diff в гите при добавлении параметра в конец. Single Abstract Method interface (Fun interface)Тоже появились в Kotlin 1.4.0. Сравните, как выглядит создание анонимного объекта для обычного и fun интерфейса: this.actionClickListener = object : BubbleView.ClickListener {
override fun onBubbleViewClick() { ... } } this.actionClickListener = BubbleView.ClickListener {
... } fun interface ClickListener {
fun onBubbleViewClick() }
val itemIdsSet: Set<String> = ...
val currentItemIds: Set<String> = ... for(itemId in itemIdsSet) { if(!currentItemIds.contains(itemId)) { repository.exclude(itemId) } } val itemIdsSet: Set<String> = ...
val currentItemIds: Set<String> = ... for (itemId in itemIdsSet subtract currentItemIds) { repository.exclude(itemId) } repository.exclude(itemIdsSet subtract currentItemIds)
https://developer.android.com/kotlin/coroutines/coroutines-best-practices =========== Источник: habr.com =========== Похожие новости:
Блог компании SimbirSoft ), #_sovershennyj_kod ( Совершенный код ), #_razrabotka_mobilnyh_prilozhenij ( Разработка мобильных приложений ), #_razrabotka_pod_android ( Разработка под Android ), #_kotlin |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 23:27
Часовой пояс: UTC + 5