Лучший опыт

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

Лучшие практики для эффективного кода на 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) }