Уже 4 декабря в Слёрме стартует поток Go для инженеров. Мы продолжаем выпускать вводные материалы, которые помогут освоить азы языка и уже после — идти в глубину. Наш предыдущий материал по теме Go — дебажим Golang с помощью Delve.
А теперь к новому материалу!
Язык программирования Go в последние годы получил широкую известность благодаря своей простоте, эффективности и поддержке асинхронного программирования. Разработанный компанией Google, язык Go был предназначен для решения общих проблем разработки программного обеспечения и облегчения написания чистого, эффективного и удобного кода. В этой статье мы рассмотрим best practices, которые помогут вам использовать Go с максимальной пользой.
1. Читаемость кода имеет значение
Одним из основных принципов Go является читаемость. Код на языке Go должен быть легко читаем и понятен. Для этого требуется:
Использовать понятные имена
Выбирайте осмысленные и понятные имена переменных и функций. Это облегчит другим (и вам в будущем) понимание вашего кода.
// Bad:
x := 42
// Good:
age := 42
Форматируйте свой код
Go поставляется со встроенным инструментом форматирования `gofmt`, который обеспечивает единый стиль кода. Используйте его для автоматического форматирования кода и поддержания единого стиля во всем проекте.
gofmt -w your_code.go
Делайте функции небольшими
Функции должны выполнять одну задачу и делать ее хорошо. Если функция становится слишком длинной или сложной, подумайте о том, чтобы разбить ее на более мелкие функции.
Вдумчиво комментируйте
Пишите четкие и лаконичные комментарии, объясняющие назначение кода. Избегайте лишних или чрезмерных комментариев. Инструментарий Go может генерировать документацию на основе комментариев, поэтому делайте их содержательными.
2. Обработка ошибок
Обработка ошибок — важнейший аспект написания надежного кода на Go.
Возврат ошибок
Функции, которые могут возвращать ошибки, должны возвращать значение типа `error`. Всегда проверяйте ошибки и обрабатывайте их соответствующим образом. Избегайте игнорирования ошибок с помощью `_`.
// Bad:
_, err := doSomething()
// Error ignored
// Good:
result, err := doSomething()
if err != nil {
// Handle the error
}
Используйте ошибки в качестве значений
В Go нет исключений. Вместо этого ошибки представляют собой значения, которые могут быть возвращены, как и любые другие значения.
Добавляйте контекст к ошибкам
При передаче ошибок по стеку вызовов используйте `fmt.Errorf` или пакет типа github.com/pkg/errors
для добавления контекста к ошибкам. Это помогает при отладке.
import (
“fmt”
“github.com/pkg/errors”
)
func someFunction() error {
err := doSomething()
if err != nil {
return errors.Wrap(err, “failed to do something”)
}
return nil
}
3. Параллелизм
Примитивы параллелизма языка Go являются его отличительной особенностью.
Используйте горутины
Горутины — это облегченные потоки в Go. С их помощью можно создавать параллельный код для задач, которые могут быть распараллелены, например, для одновременного получения данных из нескольких источников.
func main() {
go worker1()
go worker2()
// Wait for the workers to finish
time.Sleep(time.Second)
}
Общение через каналы
Каналы — это мощный способ взаимодействия между горутинами. Каналы используются для синхронизации и обмена данными. Обязательно закрывайте каналы, когда они больше не нужны, чтобы избежать аварийных ситуаций.
ch := make(chan int)
go func() {
ch <- 42
close(ch)
}()
4. Структуры и интерфейсы
Система типов Go проста и в то же время гибка. Для написания удобного кода:
Определите структуры
Используйте структуры для группировки связанных данных. Это делает ваш код более организованным и читаемым.
type Person struct {
FirstName string
LastName string
Age int
}
Выбирайте интерфейсы
Функции и методы должны принимать интерфейсы, а не конкретные типы. Это позволяет добиться большей гибкости и упростить тестирование при использовании интерфейсов.
type Logger interface {
Log(message string)
}
func DoSomething(log Logger) {
log.Log(“Doing something…”)
}
5. Тестирование
Написание тестов имеет решающее значение для обеспечения надежности вашего Go-кода:
Используйте пакет testing
Go имеет встроенный пакет тестирования, который позволяет легко писать и выполнять тесты. Пишите модульные тесты для своих функций и интеграционные тесты для своих пакетов.
func TestAdd(t *testing.T) {
result := add(2, 3)
if result != 5 {
t.Errorf(“Expected 5, but got %d”, result)
}
}
Тесты, управляемые таблицами
Для тестирования сценариев с множеством вводимых данных используйте тесты, управляемые таблицами.
func TestAdd(t *testing.T) {
testCases := []struct {
a, b int
expected int
}{
{2, 3, 5},
{0, 0, 0},
{-1, 1, 0},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf(“%d+%d”, tc.a, tc.b), func(t *testing.T) {
result := add(tc.a, tc.b)
if result != tc.expected {
t.Errorf(“Expected %d, but got %d”, tc.expected, result)
}
})
}
}
6. Управление зависимостями
Для управления зависимостями в Go введена система Go Modules.
Инициализация модуля
Чтобы запустить новый модуль в проекте, выполните команду:
go mod init module_name
При этом создается файл `go.mod`, который отслеживает ваши зависимости.
Используйте go get и go mod tidy
Для добавления зависимостей в проект используйте команду go get
. После добавления или обновления зависимостей выполните команду go mod tidy
для очистки и подтверждения актуальности файлов go.mod
и go.sum
.
7. Избегайте глобальных переменных
Глобальные переменные могут привести к тому, что код будет трудно осмыслить и протестировать. Сведите их использование к минимуму и предпочитайте передавать значения в качестве аргументов в функции и методы.
8. Профилирование и оптимизация
Go предоставляет отличные инструменты для профилирования и оптимизации кода. Используйте такие инструменты, как `pprof` и `go tool pprof` для выявления узких мест и повышения производительности.
9. Контроль версий
Всегда используйте контроль версий, например Git, для отслеживания изменений в кодовой базе. Размещайте свой проект на таких платформах, как GitHub или GitLab, для совместной работы с другими пользователями и обеспечения надежности кода.
Комментарии (5)
NeoCode
24.11.2023 07:39В Go весьма своеобразно реализовано ООП - классических "методов классов" нет, есть только "методы-расширения". И непривычно здесь то, что все функции пишутся не внутри фигурных скобок класса, а в глобальной области. Это само по себе влияет на восприятие программы. Если в чистых ОО-языках классы стоят во главе всего, и создавая новую функцию, неизбежно мыслишь категориями классов, думаешь в каком классе ее разместить, то в Go это вроде как и необязательно. Ну подумаешь, какая разница - func(obj *Foo) bar() или func bar(obj *Foo). Программа становится больше похожей на код на Си. Пока не знаю что с этим делать:)
micronull
24.11.2023 07:39функции пишутся не внутри фигурных скобок класса, а в глобальной области
В области пакета. Соответственно название пакета является неким namespace. Например
slices.Sort
, гдеslices
является названием пакета и в тоже время namespace.
ht-pro
Жиденько) Половина советов - общепринятые практики для любого языка.