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}

區別#

兩種方式很常見,在標準庫和第三方庫都可以看到,不過筆者更喜歡後者,無需在構造函數進行過多的判斷,編寫單元測試也更容易。