Какова цель golang, позволяющая несколько init в одном пакете?

Я знаю, что golang позволяет несколько init в одном пакете и даже в одном файле. Мне интересно, почему? Например, если в pkg есть много файлов, мы могли бы написать несколько init, тогда мы могли бы потеряться в том, где мы должны поместить init, и мы также можем быть смущены порядком init, если у нас есть несколько init в одном pkg. (Я имею в виду, что это лучше? У нас может быть только 1 init, тогда у нас может быть некоторый initXXX, а затем положить их в init, он выглядит довольно чистым). Какое преимущество этого сделать в представлении структуры кода?

Ответ 1

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

Если ваши исходные файлы имеют большой размер, обычно вы размещаете их содержимое (например, типы, объявления переменных, методы и т.д.) В некотором логическом порядке. Разрешение нескольких функций init() дает вам возможность поместить код инициализации рядом с частями, которые они должны инициализировать. Если это не будет разрешено, вы будете вынуждены использовать одну функцию init() для каждого пакета и помещать все в нее, вдали от переменных, которые им нужно инициализировать.

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

Если это неизбежно, вы можете создать одного "master" init(), который явно контролирует порядок других, "child" init() функций.

Пример "мастера" init(), управляющего другими функциями инициализации:

func init() {
    initA()
    initB()
}

func initA() {}
func initB() {}

В приведенном выше примере initA() всегда будет работать до initB().

Соответствующий раздел из спецификации: Инициализация пакета.

См. также связанный вопрос: Что означает лексический порядок имен файлов?

Ответ 2

Еще один вариант использования нескольких функций init() - добавление функций на основе тегов сборки. Функцию init() можно использовать для добавления хуков в существующий пакет и расширения его функциональности.

Ниже приведен сжатый пример, демонстрирующий добавление дополнительных команд в утилиту CLI на основе тегов сборки.

package main

import "github.com/spf13/cobra"

var (
    rootCmd = &cobra.Command{Use: "foo", Short: "foo"}
)

func main() {
    rootCmd.AddCommand(
        &cobra.Command{Use: "CMD1", Short: "Command1"},
        &cobra.Command{Use: "CMD1", Short: "Command1"},
    )
    rootCmd.Execute()
}

Выше приведена "ванильная" версия утилиты.

// +build debugcommands

package main

import "github.com/spf13/cobra"

func init() {
    rootCmd.AddCommand(&cobra.Command{Use: "DEBUG-CMD1", Short: "Debug command1"})
}

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

Компиляция с использованием go build -tags debugcommands создаст двоичный файл с добавленными командами, в то время как опущенный флаг -tags создаст стандартную версию.