如何優(yōu)雅的用Golang封裝配置項(Functional Options)
導(dǎo)讀?
最近要封裝一個公共服務(wù),涉及到配置項的地方總是找不到合理的方案,后來看了一下grpc在配置方面的封裝,了解到原來是golang特有的Functional Options編程模式,今天分享給大家,希望你能用到,咱們直接來看代碼
版本V1
上面代碼很容易,就是想初始化一下Server的配置選項,看起來好像沒什么問題,其實問題非常多
- 既然是初始化一些配置選項,那么當(dāng)然是有的是必選項(Addr, Port),有的是可選項(Timeout,MaxConns),可選項不選的話還得給個默認(rèn)值,顯然這種方式是不滿足的
- 因為Server的屬性都是公有方法,所以在外部任何地方都能修改屬性,存在很嚴(yán)重的代碼安全隱患
- 上面Server和main函數(shù)雖然在同一個文件里面,其實Server是作為外部包使用的,下面的case都同理 既然上面無法滿足咱們的需求,那么咱們就來修改一下
版本V2
既然配置項想要可選,那么咱們直接來個排列組合,調(diào)用不同的初始化方法即可只初始化自己想初始化的非必要選項
- Server的屬性都是私有變量,的確是解決了包的屬性被惡意篡改的行為,降低了代碼風(fēng)險
- 但是排列組合太多,新增一個屬性得新增指數(shù)級的方法,我上面的demo可選參數(shù)只有兩個timeout和maxConns,但是如果有十幾個可選參數(shù),那么需要構(gòu)造的初始化方法是非常多的
- 一般情況下,對一個工具初始化都是統(tǒng)一的方法,這樣處理的話初始化方法太多了,這一塊的內(nèi)容對使用者來說是不關(guān)心的,所以很不友好
- 不想傳的參數(shù)的默認(rèn)值依然沒有解決
版本V3
既然上面的例子封裝的初始化方法太多,那么咱們就統(tǒng)一用一個方法來解決
- 這樣做的確是解決了初始化方法太多的問題,但是太不靈活
- 比如有100個可選參數(shù),那而且你只想給最后一個可選參數(shù)賦值,但是前面99個你也得寫,寫的話具體寫幾?我既然不關(guān)心前面99個可選參數(shù),但是為什么還要寫呢?這給人感覺就很奇怪
版本V4
咱們引入一個新的結(jié)構(gòu)體Config,把必填的參數(shù)放在server里面,非必要的參數(shù)放在Congfig里面
- 解決了非必要參數(shù)可以有選擇性的傳一部分的問題,比如上面的case種只需要傳Timeout
- 也解決了不傳的參數(shù),能有默認(rèn)值的問題,比如MaxConns不傳的話 就是10
- 但是如果只傳必傳的參數(shù),那么在NewDefaultServer的時候,最后一個參數(shù)只能傳nil,傳nil的情況是不允許的,也是不友好的。
- 用這種方式的話,Config的屬性必須是公共變量,當(dāng)然就有在運行的過程中屬性被篡改的風(fēng)險
版本V5
咱們來學(xué)一學(xué)java中的builder模式
- 其實就是在Server對象外部包了一層ServerBuilder,最后在ServerBuilder.Build()中返回了Server對象
- 其實這個方法挺完美,滿足了我們之前提的全部需求,但是問題在于,golang中的err處理,在這種方式中不是很好體現(xiàn)
版本V6
接下來咱們就看一看最后的終極解決方案 FUNCTIONAL OPTIONS模式
- 這個需要注意的是 type Option func(*Server)
- 這個看起來比較整潔和優(yōu)雅,對外的接口只有一個Create。
- 相比于Builder模式,不需要引入一個Builder對象。
- 對比配置化的模式,也不需要引入一個新的Config。
總結(jié)?
Golang 由于語言本身的特性,不支持函數(shù)重載,函數(shù)式選項 的編程模式在一定程度上解決了其他語言需要通過函數(shù)重載解決的問題。函數(shù)式選項 編程有以下優(yōu)點:
- 任意順序傳遞參數(shù)
- 支持默認(rèn)值
- 向后兼容性
- 很容易維護和擴展
雖然 函數(shù)式選項 編程模式有很多優(yōu)點,但是設(shè)計模式的存在都是為了彌補語言特性的缺陷的一種手段。它是為了解決代碼擴展性的問題,往往是通過增加抽象犧牲了簡單性,切勿過度使用。有些簡單的配置,就不需要設(shè)計的這么通用了。
函數(shù)式選項模式的使用場景有哪些呢:
我們一般用來配置一些基礎(chǔ)的服務(wù)配置,比如MySQL,Redis,Kafka的配置,很多可選參數(shù),可以方便動態(tài)靈活的配置想要配置的參數(shù)。