[Совершенный код, Разработка под Android, Kotlin] Руководство по стилю Kotlin для Android разработчиков (Часть I)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
18-Янв-2021 18:30


Данная статья охватывает не только эстетические вопросы форматирования, но и другие типы соглашений и стандартов, которые необходимо знать Android разработчику.Основной фокус, в первую очередь, на жестких правилах, которым следуют Google разработчики повсеместно!Сначала я думал, что статья будет небольшой, но из-за слишком колоссального количества примеров кода она достаточно выросла.Поэтому я решил разделить её на две части.Обе части содержат описание стандартов кода на языке прораммирования Kotlin.Что покрывают обе части:
  • Именование файлов, переменных, классов, свойств и т.д.
  • Структура исходного файла
  • Форматирование - строки, пробелы, скобки, специальные конструкции, переносы и др.
  • Документация
В первой части я затрону исходные файлы и форматирование (неполностью).Ну что ж пора начинать!Исходные файлыПоговорим сначала об исходных файлах, о их структуре и других важных вещах.КодировкаВсе исходные файлы должны иметь UTF-8 кодировку. ИменованиеВсе исходные файлы, которые содержат высокоуровневые определения классов, должны именоваться следующим образом: имя класса + расширение файла .ktЕсли файл содержит несколько высокоуровневых определений (два класса и один enum к примеру) выбирается имя файла, которое описывает его содержимое:
// PhotoAdapter.kt
class PhotoAdapter(): RecyclerView.Adapter<PhotoViewHolder>() {
  // ...
}
// Utils.kt
class Utils {}
fun Utils.generateNumbers(start: Int, end: Int, step: Int) {
  // ...
}
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // ...
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // ...
СтруктураKotlin файл .kt включает в себя:
  • Заголовок, в котором указана лицензия и авторские права (необязательно)
  • Аннотации, которые объявлены на уровне файла
  • package объявление
  • import выражения
  • высокоуровневые объявления (классы, интерфейсы, различные функции)
Заголовок должен быть объявлен выше остальных определений с использованием многострочных комментариев:
/*
* Copyright 2021 MyCompany, Inc.
*
*
*/
Не используйте однострочные и KDoc комментарии:
/**
* Copyright 2021 MyCompany, Inc.
*
*/
// Copyright 2021 MyCompany, Inc.
//
Аннотация @file, которая является use-site target должна быть помещена между заголовком и package объявлением:
/*
* Copyright 2021 MyCompany, Inc.
*
*/
@file:JvmName("Foo")
package com.example.android
Оператор package и importникогда не переносятся и всегда размещаются на одной строке:
package com.example.android.fragments  // переносы запрещены
import android.view.LayoutInflater // так же и здесь
import android.view.View
Выражения import группируются для классов, функций и свойств в сортированные списки.Импорты с подстановочным знаком не разрешены:
import androidx.room.*  // так делать не нужно
Kotlin файл может содержать объявление одного или нескольких классов, функций, свойств или typealias выражений.Контент файла должен относится к одной теме. Например у нас есть публичный класс и набор extension функций, которые выполняют некоторые операции. Нет явного ограничения на количество и порядок содержимого файлаФайлы обычно читаются сверху вниз, поэтому верхние части кода должны помогать нам понять нижние.Важен логический порядок, который может объяснить сам разработчик.Например: новые функции были добавлены в конец файла, не потому что мы используем хронологический порядок, а потому что они являются вспомогательными и не зависят от другихДля членов класса применимы те же правила, что и для высокоуровневых определений.Специальные символыВ исходном коде используется только ASCII горизонтальный пробельный символ (0x20).Это означает, что:
  • Все другие пробельные символы в строчных и символьных литералах должны экранироваться
  • Tab символы не используются для отступов
Для любого символа, который имеет экранированную последовательность (\b, \r, \t, \\) используется эта последовательность, а не Unicode (например: \u000a).Для оставшихся символов, которые не принадлежат ASCII, используется либо Unicode символ (∞), либо Unicode последовательность (\u221e). Выбор зависит лишь от того, что облегчает чтение и понимание кода:
// Лучшая практика: понятно без комментариев
val symbol0 = "∞"
// Плохо: нет причины не использовать символ вместо Unicode последовательности
val symbol1 = "\u221e" // ∞
// Плохо: читатель не сможет понять, что это за символ
val symbol2 = "\u221e"
// Хорошо: использование Unicode последовательности для непечатаемого символа
return "\ufeff" + content  // неразрывный пробел нулевой ширины
ФорматированиеБлиже к коду!СкобкиСкобки не требуются дляwhen и if которые помещаются на одной строке (оператор if не имеет else ветки):
if (str.isEmpty()) return
when (option) {
    0 -> return
    // …
}
В другом случае скобки обязательно требуются для if, for, when ветвлений и do и while выражений:
if (str.isEmpty())
    return  // так делать нельзя!
if (str.isEmpty()) {
    return  // OK
}
Скобки следуют стилю Кернигана и Ритчи для непустых блоков и блочных конструкций:
  • Нельзя делать разрыв строки перед открывающей скобкой
  • Разрыв строки после открывающей cкобки
  • Разрыв строки перед закрывающей скобкой
  • Разрыв строки после закрывающей скобкой только в том случае, если она заканчивает выражение или тело функции, конструктора, класса.
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // ...
    }
}
Пустые блоки тоже должны быть в стиле K&R:
try {
    val response = fetchDogs("https://api.dog.com/dogs")
} catch (e: Exception) {} // неправильно
try {
    val response = fetchDogs("https://api.dog.com/dogs")
} catch (e: Exception) {
} // OK
if/else выражение может быть без скобок, если помещается на одной строке:
val value = if (str.isEmpty()) 0 else 1  // OK
val value = if (str.isEmpty())  // неправильно
  0
else
  1
val value = if (str.isEmpty()) {   // OK
  0
} else {
  1
}
С каждом новым блоком отступ увеличивается на 4 пробела. Когда блок закрывается отступ возвращается на предыдущий уровень (это применимо и для комментариев).ПереносыКаждое выражение разделяется переносом на новую строку (; не используется)Строка кода имеет ограничение в 100 символов.Исключения:
  • Строки, которые невозможно перенести (например: длинный URL)
  • package и import выражения
  • Команды в документации, которые можно вставить в shell
Правила для переноса на новую строку:
  • Перенос после оператора или infix функции.
  • Если строка завершается следующими операторами, то перенос осуществляется вместе с ними:
    • точка (., .?)
    • ссылка на член (::)
  • Имя метода или конструктура находится на одной строке с открывающей скобкой
  • Запятая (,) связана с элементом и не переносится
  • Стрелка (->) для lambda выражений связана с аргументами
Когда сигнатура функции не помещается, объявление параметров располагается на отдельных строчках (параметры должны иметь один отступ в 4 пробела):
fun makeSomething(
  val param1: String,
  val param2: String,
  val param3: Int
) {
}
Когда функция содержит одно выражение можно сделать так:
override fun toString(): String {
  return "Hello, $name"
}
override fun toString() = "Hello, $name"
Единственный случай, когда функция-выражение может переносится - это использование специальных блочных конструкций:
fun waitMe() = runBlocking {
  delay(1000)
}
Когда инициализация свойства не помещается на одной строке можно сделать перенос после знака присваивания (=):
val binding: ListItemBinding =
  DataBindingUtil.inflate(inflater, R.layout.list_item, parent, false)
get и set функции должны быть на отдельной строке с обычным отступом (4 пробела):
val items: LiveData<List<Item>>
  get() = _items
Read-only свойства могут иметь более краткий синтаксис:
val javaExtension: String get() = "java"
ПробелыПустая строка может быть:
  • Между членами классов: свойствами, функциями, конструкторами и другими
    • Пустая строка между двумя свойствами необязательна. Это нужно для создания логических групп (напримердля backing свойств)
  • Между выражениями для логического разделения
  • Перед первым членом функции или класса (необязательно)
Помимо требуемых правил для языка и литералов (строчных или символьных) одиночный ASCII пробел:
  • Разделяет зарезервированные слова, таких как: if, for или catch от круглой открывающей скобки:
// неправильно
for(i in 1..6) {
}
// OK
for (i in 1..6) {
}
  • Разделяет любые зарезервированные слова, таких как else и catch от закрывающей фигурной скобки:
// Неправильно
}else {
}
// OK
} else {
}
  • Ставиться перед любой открывающей фигурной скобкой:
// Неправильно
if (items.isEmpty()){
}
// OK
if (items.isEmpty()) {
}
  • Ставиться между операндами:
// Неправильно
val four = 2+2
// OK
val four = 2 + 2
// Это относится и к оператору лямбда выражения (->)
// Неправильно
items.map { item->item % 2 == 0 }
// OK
items.map { item -> item % 2 == 0 }
  • Исключение: оператор ссылка на член (::), точка (.) или range (..)
// Неправильно
val str = Any :: toString
// OK
val str = Any::toString
// Неправильно
item . toString()
// OK
item.toString()
// Неправильно
for (i in 1 .. 6) {
    println(i)
}
// OK
for (i in 1..6) {
    println(i)
}
  • Перед двоеточием (:) для указания расширения базового класса или интерфейса, а также в when выражении для generic типов:
// Неправильно
class Worker: Runnable
// OK
class Worker : Runnable
// Неправильно
fun <T> min(a: T, b: T) where T: Comparable<T>
// OK
fun <T> min(a: T, b: T) where T : Comparable<T>
  • После двоеточия (:) или запятой (,)
// Неправильно
val items = listOf(1,2)
// OK
val items = listOf(1, 2)
// Неправильно
class Worker :Runnable
// OK
class Worker : Runnable
  • По обеим сторонам двойного слеша:
// Неправильно
var debugging = false//отключен по умолчанию
// OK
val debugging = false // отключен по умолчанию
ЗаключениеДанная статья получилась довольно большая, надеюсь вам было полезно прочитанное.В следующей статье: именование, специальные конструкции и документация. Полезные ссылки: Ждите следующей части!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_sovershennyj_kod (Совершенный код), #_razrabotka_pod_android (Разработка под Android), #_kotlin, #_codestyle, #_android, #_kotlin, #_google, #_sovershennyj_kod (
Совершенный код
)
, #_razrabotka_pod_android (
Разработка под Android
)
, #_kotlin
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 22-Ноя 08:32
Часовой пояс: UTC + 5