воскресенье, 6 декабря 2020 г.

Go style guides: избегайте использования init()

По возможности избегайте init(). Когда init() неизбежен или желателен, код должен попытаться:

  • Будьте полностью детерминированными, независимо от программной среды или вызова.
  • Избегайте зависимости от порядка или побочных эффектов других функций init(). Хотя порядок init() хорошо известен, код может меняться, и, таким образом, отношения между функциями init() могут сделать код хрупким и подверженным ошибкам.
  • Избегайте доступа или манипулирования глобальным состоянием или состоянием среды, таким как машинная информация, переменные среды, рабочий каталог, программные аргументы/входные данные и т. д.
  • Избегайте операций ввода-вывода, включая вызовы файловой системы, сети и системные вызовы.

Код, который не может удовлетворить эти требования, скорее всего, принадлежит как помощник, который будет вызываться как часть main() (или где-то еще в жизненном цикле программы), или быть написанным как часть самого main(). В частности, библиотеки, которые предназначены для использования другими программами, должны быть полностью детерминированными и не выполнять "магию инициализации".

Неудачный пример:

type Foo struct {
    // ...
}

var _defaultFoo Foo

func init() {
    _defaultFoo = Foo{
        // ...
    }
}

Более удачный пример:

var _defaultFoo = Foo{
    // ...
}

// или, лучше, для тестирования:

var _defaultFoo = defaultFoo()

func defaultFoo() Foo {
    return Foo{
        // ...
    }
}

Неудачный пример:

type Config struct {
    // ...
}

var _config Config

func init() {
    // Плохо: на основе текущего каталога
    cwd, _ := os.Getwd()

    // Плохо: I/O
    raw, _ := ioutil.ReadFile(
        path.Join(cwd, "config", "config.yaml"),
    )

    yaml.Unmarshal(raw, &_config)
}

Более удачный пример:

type Config struct {
    // ...
}

func loadConfig() Config {
    cwd, err := os.Getwd()
    // обрабатываем err

    raw, err := ioutil.ReadFile(
        path.Join(cwd, "config", "config.yaml"),
    )
    // обрабатываем err

    var config Config
    yaml.Unmarshal(raw, &config)

    return config
}

Учитывая вышеизложенное, некоторые ситуации, в которых init() может быть предпочтительным или необходимым, могут включать:

  • Сложные выражения, которые нельзя представить как отдельные присваивания.
  • Подключаемые хуки, такие как диалекты database/sql, реестры типов кодирования и т. д.
  • Оптимизация Google Cloud Functions и других форм детерминированных предварительных вычислений.

Читайте также:


Комментариев нет:

Отправить комментарий