Golang允许在一个包中有多个init函数的目的是什么?

huangapple go评论83阅读模式
英文:

What's the purpose of golang allowing multiple init in one package?

问题

我知道Go语言允许在一个包甚至一个文件中有多个init函数。
我想知道为什么会这样?
例如,如果一个包有很多文件,我们可以写多个init函数,但是我们可能会迷失在应该把init函数放在哪里的问题上,如果一个包中有多个init函数,我们可能也会对init函数的执行顺序感到困惑。(我的意思是这样做是否更好?我们只能有一个init函数,然后我们可以有一些initXXX函数,然后把它们放在init函数中,这看起来更加清晰。)
在代码结构上这样做有什么优势?

英文:

I know that golang allows multiple init in one package and even in one file.
I am wondering why?
For example, if a pkg has many files, we could write multiple init then we could get lost in where to we should put init, and we could be also confused about the init order if we have multiple init in one pkg. (I mean is this better? we can only have 1 init, then we can have some initXXX, then put them into init, it seems quite clean.)
What's the advantage of doing this in code struct view?

答案1

得分: 21

这个问题可能有些主观,但使用多个包的init()函数可以使你的代码更易读和维护。

如果你的源文件很大,通常会按照一定的逻辑顺序排列它们的内容(例如类型、变量声明、方法等)。允许使用多个init()函数可以让你将初始化代码放在它们应该初始化的部分附近。如果不允许这样做,你将被迫在每个包中使用一个单独的init()函数,并将所有内容都放在其中,远离它们需要初始化的变量。

是的,使用多个init()函数可能需要注意执行顺序,但要知道使用多个init()函数并不是必需的,它只是一种可能性。你可以编写init()函数,使其不产生“副作用”,不依赖于其他init()函数的完成。

如果无法避免依赖关系,你可以创建一个“主”init()函数,显式地控制其他“子”init()函数的执行顺序。

下面是一个控制其他初始化函数的“主”init()函数的示例:

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

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

在上面的示例中,initA()将始终在initB()之前运行。

相关规范的章节:包的初始化

还可以参考相关问题:https://stackoverflow.com/questions/31650965/what-does-lexical-file-name-order-mean/31651094#31651094

英文:

This question may be somewhat opinion based, but using multiple package init() functions can make your code easier to read and maintain.

If your source files are large, usually you arrange their content (e.g. types, variable declarations, methods etc.) in some logical order. Allowance of multiple init() functions gives you the possibility to put initialization code near to the parts they ought to initialize. If this would not be allowed, you would be forced to use a single init() function per package, and put everything in it, far from the variables they need to initialize.

Yes, having multiple init() functions may require some care regarding the execution order, but know that using multiple init() functions is not a requirement, it's just a possibility. And you can write init() functions to not have "side" effects, to not rely on the completion of other init() functions.

If that is unavoidable, you can create one "master" init() which explicitly controls the order of other, "child" init() functions.

An example of a "master" init() controlling other initialization functions:

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

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

In the above example, initA() will always run before initB().

Relevant section from spec: Package initialization.

Also see related question: https://stackoverflow.com/questions/31650965/what-does-lexical-file-name-order-mean/31651094#31651094

答案2

得分: 9

多个init()函数的另一个用例是基于构建标签添加功能。init()函数可用于向现有包添加钩子,并扩展其功能。

以下是一个简化的示例,演示了基于构建标签向CLI实用程序添加更多命令的过程。

package main

import "github.com/spf13/cobra"

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

func init() {
    rootCmd.AddCommand(
        &cobra.Command{Use: "CMD1", Short: "Command1"},
        &cobra.Command{Use: "CMD2", Short: "Command2"},
    )
}

func main() {
    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标志将生成标准版本。

英文:

Another use case for multiple init() functions is adding functionality based on build tags. The init() function can be used to add hooks into the existing package and extend its functionality.

The following is a condensed example demonstrating the addition of more commands to a CLI utility based on build tags.

package main

import "github.com/spf13/cobra"

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

func init() {
	rootCmd.AddCommand(
		&cobra.Command{Use: "CMD1", Short: "Command1"},
		&cobra.Command{Use: "CMD2", Short: "Command2"},
	)
}

func main() {
	rootCmd.Execute()
}

The above is the "vanilla" version of the utility.

// +build debugcommands

package main

import "github.com/spf13/cobra"

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

The contents of the second file extends the standard command with additional commands that are mostly relevant during development.

Compiling using go build -tags debugcommands will produce a binary with the added commands, while omitting the -tags flag will produce a standard version.

huangapple
  • 本文由 发表于 2017年7月24日 18:44:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/45278540.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定