翻譯自:https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/

Go中的接口允許我們將不同的類型暫時(shí)視為同一數(shù)據(jù)類型。它們是 Go 程序員工具箱的核心,但新的 Go 開發(fā)者往往會(huì)使用不當(dāng)......導(dǎo)致代碼難以閱讀,易于產(chǎn)生Bug。讓我們來看看Golang接口的一些最佳實(shí)踐。
我經(jīng)常以標(biāo)準(zhǔn)庫(kù)為例,來說明如何寫出干凈的Go接口。標(biāo)準(zhǔn)的錯(cuò)誤接口很簡(jiǎn)單:
type error interface {
Error() string
}
error接口封裝了任何有Error()方法的類型。該方法不接受任何參數(shù),并返回一個(gè)字符串。例如,讓我們定義一個(gè)表示網(wǎng)絡(luò)問題的結(jié)構(gòu)。
type networkProblem struct {
message string
code int
}
然后,我們定義一個(gè)Error()方法:
func (np networkProblem) Error() string {
return fmt.Sprintf("network error! message: %s, code: %v", np.message, np.code)
}
Now, we can use an instance of the networkProblem struct wherever an error is accepted.
現(xiàn)在,我們可以在任何接受到錯(cuò)誤的地方使用networkProblem結(jié)構(gòu)的實(shí)例:
func handleErr(err error) {
fmt.Println(err.Error())
}
np := networkProblem{
message: "we received a problem",
code: 404,
}
handleErr(np)
// prints "network error! message: we received a problem, code: 404"
堅(jiān)持小接口
如果你只能從這篇文章中得到一條建議,那就是:讓接口小一點(diǎn)! 接口的目的是為了定義準(zhǔn)確表示一個(gè)想法或概念所必需的最小行為。
下面是標(biāo)準(zhǔn)HTTP包中一個(gè)更大的接口的例子,它是定義最小行為的好例子:
type File interface {
io.Closer
io.Reader
io.Seeker
Readdir(count int) ([]os.FileInfo, error)
Stat() (os.FileInfo, error)
}
任何滿足接口行為的類型都可以被HTTP包當(dāng)作一個(gè)文件來處理。這很方便,因?yàn)镠TTP包不需要知道它處理的是磁盤上的文件、網(wǎng)絡(luò)緩沖區(qū),還是簡(jiǎn)單的[]字節(jié)。
接口應(yīng)該沒有滿足類型的知識(shí)
一個(gè)接口不應(yīng)該關(guān)心具體的類型。
下面,假設(shè)我們構(gòu)建一個(gè)用于描述一輛汽車的組件的接口:
type car interface {
GetColor() string
GetSpeed() int
IsFiretruck() bool
}
GetColor() 和 GetSpeed() 方法是汽車領(lǐng)域的知識(shí)。而 IsFiretruck() 則是反模式。此接口應(yīng)該關(guān)注所有汽車的通用方法,而不應(yīng)該關(guān)心它是否是一臺(tái)消防車。 否則,我們還必須在這個(gè)接口中增加:IsPickup(), IsSedan(), IsTank() 等等,沒完沒了了。
相反,當(dāng)給定一個(gè)car的接口實(shí)例時(shí),開發(fā)應(yīng)該根據(jù)類型斷言的原生功能來推斷出子類型?;蛘撸绻咏涌谛枰粋€(gè)子接口,它可以定義為:
type firetruck interface {
car
HoseLength() int
}
firetruck接口繼承了汽車的必要方法,并增加了一個(gè)額外的必要方法,使汽車成為消防車。
接口不是類
接口不是類,應(yīng)該是小的。
接口不需要構(gòu)造函數(shù)或析構(gòu)函數(shù),因?yàn)樗鼪]有必要進(jìn)行數(shù)據(jù)的初始化和銷毀。
接口在本質(zhì)上不是分層的,盡管有語(yǔ)法糖來創(chuàng)建接口,而這些接口恰好是其他接口的超集。
接口只負(fù)責(zé)定義函數(shù)簽名,不關(guān)心其具體實(shí)現(xiàn)。在結(jié)構(gòu)方法中,定義一個(gè)接口可以減少重復(fù)代碼。例如,如果5種類型實(shí)現(xiàn)了錯(cuò)誤接口,它們就需要分別實(shí)現(xiàn)Error()函數(shù)。