翻譯自:https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/
Go中的接口允許我們將不同的類型暫時視為同一數(shù)據(jù)類型痒筒。它們是 Go 程序員工具箱的核心,但新的 Go 開發(fā)者往往會使用不當......導致代碼難以閱讀柄瑰,易于產(chǎn)生Bug。讓我們來看看Golang接口的一些最佳實踐舔糖。
我經(jīng)常以標準庫為例铅鲤,來說明如何寫出干凈的Go接口蕉朵。標準的錯誤接口很簡單:
type error interface {
Error() string
}
error接口封裝了任何有Error()方法的類型。該方法不接受任何參數(shù)关顷,并返回一個字符串糊秆。例如,讓我們定義一個表示網(wǎng)絡(luò)問題的結(jié)構(gòu)议双。
type networkProblem struct {
message string
code int
}
然后痘番,我們定義一個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)在,我們可以在任何接受到錯誤的地方使用networkProblem結(jié)構(gòu)的實例:
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"
堅持小接口
如果你只能從這篇文章中得到一條建議平痰,那就是:讓接口小一點! 接口的目的是為了定義準確表示一個想法或概念所必需的最小行為汞舱。
下面是標準HTTP包中一個更大的接口的例子,它是定義最小行為的好例子:
type File interface {
io.Closer
io.Reader
io.Seeker
Readdir(count int) ([]os.FileInfo, error)
Stat() (os.FileInfo, error)
}
任何滿足接口行為的類型都可以被HTTP包當作一個文件來處理宗雇。這很方便昂芜,因為HTTP包不需要知道它處理的是磁盤上的文件、網(wǎng)絡(luò)緩沖區(qū)赔蒲,還是簡單的[]字節(jié)泌神。
接口應該沒有滿足類型的知識
一個接口不應該關(guān)心具體的類型。
下面舞虱,假設(shè)我們構(gòu)建一個用于描述一輛汽車的組件的接口:
type car interface {
GetColor() string
GetSpeed() int
IsFiretruck() bool
}
GetColor()
和 GetSpeed()
方法是汽車領(lǐng)域的知識欢际。而 IsFiretruck()
則是反模式。此接口應該關(guān)注所有汽車的通用方法矾兜,而不應該關(guān)心它是否是一臺消防車损趋。 否則,我們還必須在這個接口中增加:IsPickup()
, IsSedan()
, IsTank()
等等椅寺,沒完沒了了浑槽。
相反墙杯,當給定一個car的接口實例時,開發(fā)應該根據(jù)類型斷言的原生功能來推斷出子類型括荡。或者溉旋,如果子接口需要一個子接口畸冲,它可以定義為:
type firetruck interface {
car
HoseLength() int
}
firetruck接口繼承了汽車的必要方法,并增加了一個額外的必要方法观腊,使汽車成為消防車邑闲。
接口不是類
接口不是類,應該是小的梧油。
接口不需要構(gòu)造函數(shù)或析構(gòu)函數(shù)苫耸,因為它沒有必要進行數(shù)據(jù)的初始化和銷毀。
接口在本質(zhì)上不是分層的儡陨,盡管有語法糖來創(chuàng)建接口褪子,而這些接口恰好是其他接口的超集。
接口只負責定義函數(shù)簽名骗村,不關(guān)心其具體實現(xiàn)嫌褪。在結(jié)構(gòu)方法中,定義一個接口可以減少重復代碼胚股。例如笼痛,如果5種類型實現(xiàn)了錯誤接口,它們就需要分別實現(xiàn)Error()函數(shù)琅拌。