避免使用 `init()`
发布者:admin 发表于:439天前 阅读数:619 评论:0

尽可能避免使用init()。当init()是不可避免或可取的,代码应先尝试:

无论程序环境或调用如何,都要完全确定。

避免依赖于其他init()函数的顺序或副作用。虽然init()顺序是明确的,但代码可以更改,

因此init()函数之间的关系可能会使代码变得脆弱和容易出错。

避免访问或操作全局或环境状态,如机器信息、环境变量、工作目录、程序参数/输入等。

避免I/O,包括文件系统、网络和系统调用。

不能满足这些要求的代码可能属于要作为main()调用的一部分(或程序生命周期中的其他地方),

或者作为main()本身的一部分写入。特别是,打算由其他程序使用的库应该特别注意完全确定性,

而不是执行“init magic”

Bad Good

type Foo struct { // ... } var _defaultFoo Foo func init() { _defaultFoo = Foo{ // ... } } |

var _defaultFoo = Foo{ // ... } // or, 为了更好的可测试性: var _defaultFoo = defaultFoo() func defaultFoo() Foo { return Foo{ // ... } }

type Config struct { // ... } var config Config func init() { // Bad: 基于当前目录 cwd, := os.Getwd() // Bad: 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() // handle err raw, err := ioutil.ReadFile( path.Join(cwd, "config", "config.yaml"), ) // handle err var config Config yaml.Unmarshal(raw, &config) return config } |

考虑到上述情况,在某些情况下,init()可能更可取或是必要的,可能包括:

不能表示为单个赋值的复杂表达式。

可插入的钩子,如database/sql、编码类型注册表等。

Google Cloud Functions和其他形式的确定性预计算的优化。