Go 编写优雅的构造函数

本页内容

Go 编写优雅的构造函数。

假设有一个名为 ViewManager 的结构体如下:

 1 type ViewManager struct {
 2    fs http.FileSystem // 文件系统
 3    suffix string // 视图文件的后缀
 4    cache bool // 是否开启缓存
 5}
 6
 7func New(fs http.FileSystem) *ViewManager {
 8    return &ViewManager{
 9        fs: fs,
10        cache: true,
11        suffix: ".tmpl",
12    }
13}

其所有成员都是不可写的,在我们需要创建不同 suffix、cache 的实例时,我们可以轻易地通过新增构造函数来实现,如 NewSuffix(fs http.FileSystem, suffix string)NewCache(fs http.FileSystem, cache bool),但是这样看起来繁琐不美观。其实我们可以通过一个构造函数来实现。

配置结构

第一种很简单,定义一个配置结构,然后传入到构造函数即可。

 1type Config struct {
 2    Suffix string
 3    Cache bool
 4}
 5
 6func New(fs http.FileSystem, cfg Config) *ViewManager {
 7    m := &ViewManager{
 8        fs: fs,
 9        cache: cfg.Cache,
10        suffix: ".tmpl",
11    }
12
13    if cfg.Suffix != "" {
14        m.suffix = suffix
15    }
16
17    return m
18}

配置函数

配置函数和配置结构类似,只是利用函数的方式进行设置:

 1type Option func(*ViewManager)
 2
 3func Suffix(v string) Option {
 4    return func(m *ViewManager) {
 5        m.suffix = v
 6    }
 7}
 8
 9func Cache(v bool) Option {
10    return func(m *ViewManager) {
11        m.cache = v
12    }
13}
14
15func New(fs http.FileSystem, opts ... Option) *ViewManager {
16    m := &ViewManager{
17        fs: fs,
18        cache: true,
19        suffix: ".tmpl",
20    }
21
22    for _, f := range opts {
23        f(m)
24    }
25
26    return m
27}

区别

两种方式很常见,在标准库和第三方库都可以看到,不过笔者更喜欢后者,无需在构造函数进行过多的判断,编写单元测试也更容易。