[Совершенный код, Разработка под Android, Kotlin] Руководство по стилю Kotlin для Android разработчиков (Часть I)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Данная статья охватывает не только эстетические вопросы форматирования, но и другие типы соглашений и стандартов, которые необходимо знать 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 // отключен по умолчанию
ЗаключениеДанная статья получилась довольно большая, надеюсь вам было полезно прочитанное.В следующей статье: именование, специальные конструкции и документация. Полезные ссылки:
- Kotlin style guide(на английском)
- K&R стиль
- Книга: Чистый код (Боб Мартин)
- Кратко о книге Боба Мартина
Ждите следующей части!
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Разработка игр, Разработка под Android, Unity, Дизайн игр] Как Google Play разрушил все ожидания. Опыт создания игры на Android. 2 месяца разработки. Отказ. Временный бан Admob
- [Контекстная реклама, IT-компании] СМИ: Facebook и Google заключили тайное соглашение, которое сократило конкуренцию в рекламе; Google все отрицает
- [Венчурные инвестиции, Развитие стартапа, Финансы в IT, IT-компании] Новости IT и стартапов: продолжается охота на Трампа, правда о китайском рейтинге
- [Google Chrome, API, Браузеры, IT-компании] 15 марта в некоторых браузерах на Chromium сломается синхронизация: Google меняет доступ к приватным API
- [Поисковые технологии, Работа с видео, Копирайт] Google отклонила уведомления об удалении платформ копирования звука с YouTube по закону DMCA
- [Разработка под Android, Смартфоны, Софт] Как удалить «неудаляемые» приложения со смартфона
- [Смартфоны] «Коммерсантъ»: сервисы Google вернутся на телефоны Honor
- [Совершенный код, Assembler, Системное программирование, Компиляторы, Реверс-инжиниринг] И на Солнце есть пятна
- [Разработка под iOS, Разработка игр, Разработка под Android, Unity, Игры и игровые приставки] ALT CITY: Online. Как я в одиночку создавал “GTA Online” для мобильных устройств. Часть 2
- [Java, Параллельное программирование, Kotlin] Лечим Java Reactor при помощи Kotlin Coroutines
Теги для поиска: #_sovershennyj_kod (Совершенный код), #_razrabotka_pod_android (Разработка под Android), #_kotlin, #_codestyle, #_android, #_kotlin, #_google, #_sovershennyj_kod (
Совершенный код
), #_razrabotka_pod_android (
Разработка под Android
), #_kotlin
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:34
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Данная статья охватывает не только эстетические вопросы форматирования, но и другие типы соглашений и стандартов, которые необходимо знать Android разработчику.Основной фокус, в первую очередь, на жестких правилах, которым следуют Google разработчики повсеместно!Сначала я думал, что статья будет небольшой, но из-за слишком колоссального количества примеров кода она достаточно выросла.Поэтому я решил разделить её на две части.Обе части содержат описание стандартов кода на языке прораммирования Kotlin.Что покрывают обе части:
// 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> = // ...
/*
* Copyright 2021 MyCompany, Inc. * * */ /**
* Copyright 2021 MyCompany, Inc. * */ // Copyright 2021 MyCompany, Inc. // /*
* Copyright 2021 MyCompany, Inc. * */ @file:JvmName("Foo") package com.example.android package com.example.android.fragments // переносы запрещены
import android.view.LayoutInflater // так же и здесь import android.view.View import androidx.room.* // так делать не нужно
// Лучшая практика: понятно без комментариев
val symbol0 = "∞" // Плохо: нет причины не использовать символ вместо Unicode последовательности val symbol1 = "\u221e" // ∞ // Плохо: читатель не сможет понять, что это за символ val symbol2 = "\u221e" // Хорошо: использование Unicode последовательности для непечатаемого символа return "\ufeff" + content // неразрывный пробел нулевой ширины if (str.isEmpty()) return
when (option) { 0 -> return // … } if (str.isEmpty())
return // так делать нельзя! if (str.isEmpty()) { return // OK }
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) // ... } } 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 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 }
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) val items: LiveData<List<Item>>
get() = _items val javaExtension: String get() = "java"
// неправильно
for(i in 1..6) { } // OK for (i in 1..6) { }
// Неправильно
}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 }
// Неправильно
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) }
// Неправильно
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 =========== Похожие новости:
Совершенный код ), #_razrabotka_pod_android ( Разработка под Android ), #_kotlin |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 14:34
Часовой пояс: UTC + 5