Лучший опыт

Лучшие практики для эффективного кода на Golang. Часть 1.

Введение Потратьте всего 12 минут, чтобы писать эффективный код на Go. № 1: правильный отступ С хорошим отступом код удобнее для восприятия. Используйте символы табуляции или пробелы последовательно, отдавая предпочтение символам табуляции, и следуйте стандартному соглашению Go по применению отступов. package main import "fmt" func main() { for i := 0; i < 5; i++ { fmt.Println("Hello, World!") } } Форматируйте код автоматически с отступом по стандарту Go, запуская g
Лучшие практики для эффективного кода на Golang. Часть 1...

Введение

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

№ 1: правильный отступ

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

package main  import "fmt"  func main() {     for i := 0; i < 5; i++ {         fmt.Println("Hello, World!")     } }

Форматируйте код автоматически с отступом по стандарту Go, запуская gofmt:

$ gofmt -w your_file.go

№ 2: правильный импорт пакетов

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

package main  import (     "fmt"     "math/rand"     "time" )

№ 3: информативные названия переменных и функций

  1. Используйте содержательные названия, передающие назначение переменной.
  2. CamelCase: начинайте со строчной буквы, а первую букву каждого последующего слова в названии делайте заглавной.
  3. Для временных переменных с небольшой областью действия допустимы короткие, лаконичные названия.
  4. Избегайте непонятных аббревиатур и акронимов в пользу информативных названий.
  5. Сохраняйте единообразие именования во всей кодовой базе.
package main  import "fmt"  func main() {     // Объявляем переменные с содержательными названиями     userName := "John Doe" // CamelCase: начинаем со строчной буквы, а последующие слова с заглавной.     itemCount := 10 // Короткие, лаконичные названия для переменных с небольшой областью действия.     isReady := true // Избегаем непонятных аббревиатур и акронимов.      // Отображаем значения переменных     fmt.Println("User Name:", userName)     fmt.Println("Item Count:", itemCount)     fmt.Println("Is Ready:", isReady) }  // Для переменных, описанных в спецификации пакета, используем mixedCase: начинаем со строчной буквы, а следующее слово с заглавной. var exportedVariable int = 42  // Названия функций должны быть информативными func calculateSumOfNumbers(a, b int) int {     return a + b }  // Сохраняем единообразие именования во всей кодовой базе.

№ 4: ограничение длины строки

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

package main  import (     "fmt"     "math" )  func main() {     result := calculateHypotenuse(3, 4)     fmt.Println("Hypotenuse:", result) }  func calculateHypotenuse(a, b float64) float64 {     return math.Sqrt(a*a + b*b) }

Примечание: есть мнение о необходимости ограничивать строки кода 120, 150 и даже 180 символами.

№ 5: константы для магических значений

Избегайте магических значений в коде. Это жестко заданные числа или строки, разбросанные по всему коду, из-за нехватки контекста трудно понять их назначение. Чтобы повысить сопровождаемость кода, определяйте для них константы:

package main  import "fmt"  const (     // Определяем константу для максимального числа повторных попыток     MaxRetries = 3      // Определяем константу для времени ожидания по умолчанию в секундах     DefaultTimeout = 30 )  func main() {     retries := 0     timeout := DefaultTimeout      for retries < MaxRetries {         fmt.Printf("Attempting operation (Retry %d) with timeout: %d seconds\n", retries+1, timeout)          // ... Логика кода здесь ...          retries++     } }

№ 6: обработка ошибок

В Go разработчикам рекомендуется обрабатывать ошибки явно. И вот причины:

  1. Безопасность: при обработке ошибок гарантируется, что неожиданные проблемы не приведут к панике или внезапному аварийному завершению программы.
  2. Четкость: с явной обработкой ошибок код удобнее для восприятия, проще определить места возникновения ошибок.
  3. Отладка: при обработке ошибок получается ценная информация для отладки и устранения проблем.

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

package main  import (     "fmt"     "os" )  func main() {     // Открываем файл     file, err := os.Open("example.txt")     if err != nil {         // Обрабатываем ошибку         fmt.Println("Error opening the file:", err)         return     }     defer file.Close() // По завершении файл закрываем      // Считываем из файла     buffer := make([]byte, 1024)     _, err = file.Read(buffer)     if err != nil {         // Обрабатываем ошибку         fmt.Println("Error reading the file:", err)         return     }      // Выводим содержимое файла     fmt.Println("File content:", string(buffer)) }

Примечание: пример совершенствуется добавлением обработки ошибок в функцию defer при вызове file.Close()  —  имеется возможность возвращения ошибки.

№ 7: глобальные переменные

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

Как избегать глобальных переменных, проиллюстрируем простой программой на Go:

package main  import (     "fmt" )  func main() {     // Объявляем и инициализируем переменную в функции «main»     message := "Hello, Go!"      // Вызываем функцию, использующую локальную переменную     printMessage(message) }  // «printMessage» — это функция, принимающая параметр func printMessage(msg string) {     fmt.Println(msg) }

№ 8: структуры для сложных данных

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

Вот полный пример программы с применением структур в Go:

package main  import (     "fmt" )  // Определяем структуру «Person» для представления информации о человеке. type Person struct {     FirstName string // Имя     LastName string // Фамилия     Age int // Возраст }  func main() {     // Создаем экземпляр структуры «Person» и инициализируем ее поля.     person := Person{         FirstName: "John",         LastName: "Doe",         Age: 30,     }      // Получаем доступ к значениям полей структуры и выводим их.     fmt.Println("First Name:", person.FirstName) // Выводим имя     fmt.Println("Last Name:", person.LastName) // Выводим фамилию     fmt.Println("Age:", person.Age) // Выводим возраст }

№ 9: комментарии

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

Однострочные комментарии начинаются с //, комментируйте ими конкретные строки кода:

package main  import "fmt"  func main() {     // Это однострочный комментарий     fmt.Println("Hello, World!") // Выводим приветствие }

Многострочные комментарии помещаются между символами /* и */, эти комментарии длиннее или занимают несколько строк:

package main  import "fmt"  func main() {     /*     Это многострочный комментарий.     Он может занимать несколько строк.     */     fmt.Println("Hello, World!") // Выводим приветствие }

Комментарии к функциям добавляются для объяснения их назначения, параметров и возвращаемых значений, для этих комментариев используйте стиль godoc:

package main  import "fmt"  // «greetUser» приветствует пользователя по имени. // Параметры: // имя (строка): имя приветствуемого пользователя. // Возвращается: // строка: приветственное сообщение. func greetUser(name string) string {     return "Hello, " + name + "!" }  func main() {     userName := "Alice"     greeting := greetUser(userName)     fmt.Println(greeting) }

Комментарии к пакету добавляются в верхней части файлов Go для описания назначения пакета, используйте тот же стиль godoc:

package main  import "fmt"  // Это пакет «main» программы Go. // Он содержит функцию точки входа «main». func main() {     fmt.Println("Hello, World!") }

№ 10: горутины для параллельного выполнения

Чтобы параллельные операции выполнялись эффективно, используйте горутины. Это легкие, параллельно выполняемые потоки на Go, благодаря которым функции запускаются одновременно. И без накладных расходов, характерных для традиционных потоков. С горутинами пишутся эффективные программы с высокой степенью параллелизма.

Вот простой пример:

package main  import (     "fmt"     "time" )  // Функция, запускаемая параллельно func printNumbers() {     for i := 1; i <= 5; i++ {         fmt.Printf("%d ", i)         time.Sleep(100 * time.Millisecond)     } }  // Функция, запускаемая в горутине «main» func main() {     // Запускаем горутину     go printNumbers()      // Продолжаем выполнение «main»     for i := 0; i < 2; i++ {         fmt.Println("Hello")         time.Sleep(200 * time.Millisecond)     }     // Выполнение горутины обязательно завершается перед выходом     time.Sleep(1 * time.Second) }