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