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


53   0  

Comments

    Ничего не найдено.