原文:https://spino.tech/blog/important-go-interfaces/#golang
在Go語言中,接口是一個非常重要的概念枣接。它們提供了一種簡單而有效的方式來表達類型間的共同行為纳决。對于我們需要某種多態(tài)性的典型情況祈秕,它們給我們提供了易于理解的解決方案牺堰。這就是Golang開發(fā)人員一直在使用接口的原因梁呈。
一些接口比其他接口更特別。Go標(biāo)準(zhǔn)庫中定義了最基本的残吩。它們被使用并可以在每個Go項目中找到财忽。每個Golang開發(fā)者都應(yīng)該知道這些最重要的接口倘核。通過這種方式泣侮,人們可以通過查看方法簽名來輕松確定給定類型實現(xiàn)哪個知名接口。它還使我們能夠掌握在調(diào)用所有標(biāo)準(zhǔn)和使用的接口的實現(xiàn)方法時可以期待的行為紧唱。標(biāo)準(zhǔn)接口還向我們展示了如何設(shè)計良好的接口(其中一個是慣用的Go代碼)活尊。
在這篇博客文章中,我將介紹一些最重要和有用的知識接口和語義漏益。
在此之后蛹锰,一篇稍長的介紹讓我們看一個實際的列表:
內(nèi)置界面 - error
錯誤是一個內(nèi)置的接口,它描述可被視為錯誤值的類型绰疤。錯誤接口定義為:
type error interface {
Error() string
}
正如你所看到的铜犬,這是一個非常簡單的接口。用于描述某種錯誤的Go中的每個類型都必須實現(xiàn)一個方法 - Error()轻庆。它的目的是提供有關(guān)給定錯誤的精確信息癣猾,包括詳細上下文。
大多數(shù)時候你不需要自己創(chuàng)建這個接口的實現(xiàn)余爆。你可以在包中找到helper方法errors纷宇。例如,要創(chuàng)建新的錯誤值蛾方,可以這樣寫:
myError := errors.New("Something goes wrong")
如果你想包裝另一個錯誤的錯誤像捶,并提供給它,你可以使用功能更多的上下文Errorf從fmt包
if err != nil {
return fmt.Errorf("Error occured: %v", err)
}
如果您正在尋找更強大的解決方案桩砰,可以幫助您有效處理Go中的錯誤拓春,則可以使用 https://github.com/pkg/errors/軟件包。通過使用Wrap
該包中的函數(shù)亚隅,您可以創(chuàng)建含有功能堆棧跟蹤的有意義的錯誤消息硼莽。這個解決方案比使用更好fmt.Errorf
。
io.Reader
該接口對于各種類型的文件系統(tǒng)和網(wǎng)絡(luò)通信任務(wù)非常重要枢步。它是這樣定義的:
type Reader interface {
Read(p []byte) (n int, err error)
}
其定義包含一種方法Read()沉删。該方法將從len(p)其定義的源中讀取字節(jié)渐尿。字節(jié)將被保存在切片中p []byte。如果n出現(xiàn)錯誤矾瑰,此方法將返回讀取的字節(jié)數(shù)()和錯誤(err)砖茸。
例如,如果您打開一個文件然后調(diào)用該Read()方法殴穴,您將從該文件中讀取字節(jié):
file, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
content := make([]byte, 10)
// Try to read 10 or less bytes in case of EOF
n, err := file.Read(content)
此方法也具有相同的網(wǎng)絡(luò)連接語義凉夯,您可以從中讀取數(shù)據(jù),就像從文件一樣采幌。
一個ioutil
包定義了一種方法ReadAll
劲够,當(dāng)你想一次讀取整個文件時[ doc ](或者直到EOF
從任何實現(xiàn)io.Reader
接口的源代碼讀取)都會有幫助休傍。
...
file, err := os.Open("file.txt")
...
// ReadAll argument is io.Reader.
// It turns out that struct os.File is implementing this interface,
// as we saw before, so we can use it here.
b, err := ioutil.ReadAll(file),
if err != nil {
// handle error
}
// b slice contains all bytes of file
通過使用io.Reader接口征绎,我們可以將其實現(xiàn)之一封裝在另一個實現(xiàn)中。這給了我們一種實現(xiàn)諸如以下方面的習(xí)慣方式:
從壓縮文件中讀取
從壓縮網(wǎng)絡(luò)中讀取TCP流
從加密的網(wǎng)絡(luò)連接讀取數(shù)據(jù)
以下是從壓縮文件中讀取的示例:
import "compress/gzip"
...
file, err := os.Open("archive.gz")
...
// Wrap os.File with gzip.Reader
// We can do this beacause gzip.NewReader expects io.Reader implementation
// as argument and os.File is implementing it
decompressReader, err := gzip.NewReader(file)
c := make([]byte, 10)
// Read 10 decompressed bytes
n, err := decompressReader.Read(c)
if err != nil {
// handle errors
}
a := c[0] // use decompressed data
io.Writer
這個接口與io.Reader非常相似磨取。我們用它來寫入字節(jié)到各個目的地人柿。其定義也非常簡單:
type Writer interface {
Write(p []byte) (n int, err error)
}
這個接口有一個方法 - - Write()接受一個參數(shù) - bytes p([]byte)的片段。然后它將這部分字節(jié)寫入某個定義了該方法的輸出忙厌。最后凫岖,它返回n- 已寫入輸出的字節(jié)數(shù),以及寫入error期間是否有錯誤逢净。io.Writer我使用簡單的例子包括將文件寫入文件或網(wǎng)絡(luò)連接哥放。
此示例顯示如何將文本寫入'Test\n'文件:
...
file, err := os.Create("file.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
content := []byte("Test\n")
n, err := file.Write(content)
if err != nil {
log.Printf("Error while writeing to file: %v", err)
}
...
類似于io.Reader,io.Writer接口可以彼此纏繞爹土。這給了我們與以下相反的結(jié)果io.Reader甥雕,例如:
將壓縮字節(jié)寫入文件
將壓縮字節(jié)寫入網(wǎng)絡(luò)連接
這個例子展示了我們?nèi)绾螌嚎s字節(jié)寫入文件:
import "compress/gzip"
...
file, err := os.Create("file.txt.gz")
if err != nil {
log.Fatal(err)
}
defer file.Close()
content := []byte("Test\n")
// Wrap os.File with gzip.Writer
compressedWriter := gzip.NewWriter(file)
// Write compressed bytes
n, err := compressedWriter.Write(content)
if err != nil {
log.Printf("Error while writeing to file: %v", err)
}
...
io.ReadWriter
這是第一個呈現(xiàn)的界面,它是Golang界面組成的例子着饥。這個接口是這樣定義的:
type ReadWriter interface {
Reader
Writer
}
正如你所看到的犀农,這個接口由兩個其他接口組成:
io.Reader
io.Writer
它表示為可以讀取和寫入的內(nèi)容定義的方法集。例如:
os.File
bytes.Buffer
通過定義io.Reader和io.Writer小一個方法的接口宰掉,我們現(xiàn)在可以編寫他們進入一個新的呵哨。
io.Closer
該接口是為使用后需要關(guān)閉的對象定義的。立即想到的一個例子是os.File轨奄。這個接口定義非常簡單:
type Closer interface {
Close() error
}
在這個界面中孟害,我們只有一種方法 - Close。它用于報告給定資源的使用完成情況挪拟。當(dāng)我們使用緩沖的io(package bufio)寫入文件時挨务,這種方法也很重要,我們需要確保所有字節(jié)都保存到文件中。
方法Close與defer關(guān)鍵字一起用于很多情況:
func foo() {
f, err := os.Open("file.txt")
if err != nil {
//error handling
}
// Call Close() when we will be returning form current function
defer func() {
err := f.Close()
if err != nil {
// error when closing file
}
}()
...
}
io.WriteCloser
這是接口的下一個例子谎柄,將兩個簡單的接口組合成一個更大的接口丁侄。這個接口是這樣定義的:
type WriteCloser interface {
Writer
Closer
}
它結(jié)合了io.Writer和的功能io.Closer。
io.ReadWriteCloser
該界面將三個簡單界面組合在一起
type ReadWriteCloser interface {
Reader
Writer
Closer
}
fmt.Stringer
這個接口功能類似于str()Python和toString()Java中的方法朝巫。它用于定義給定對象的文本表示鸿摇。這個接口有一個方法String():
type Stringer interface {
String() string
}
當(dāng)對象被傳遞給該方法被隱含地調(diào)用fmt.Printf 功能和動詞是有效的字符串(%s,%q劈猿,%v拙吉,%x,%X)揪荣。請注意筷黔,如果一個對象實現(xiàn)了兩者String()和Error()方法,那么該Error()方法將被使用fmt.Printf仗颈。
fmt.GoStringer
此接口可用于以fmt.Printf格式string(%#v)更改Go語法表示動詞的行為佛舱。默認情況下,這個動詞將產(chǎn)生一個有效的Go源代碼對象的表示揽乱。如果你想改變這個名眉,那么你需要實現(xiàn)這個接口:
type GoStringer interface {
GoString() string
}
net.Conn
這個界面比以前更復(fù)雜粟矿。它有更多的方法凰棉,它們被設(shè)計用于處理網(wǎng)絡(luò)數(shù)據(jù)流。
type Conn interface {
Read(b []byte) (n int, err error)
Write(b []byte) (n int, err error)
Close() error
LocalAddr() Addr
RemoteAddr() Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
這net.Conn 是一個接口陌粹,因為這樣很容易測試使用網(wǎng)絡(luò)進行通信的程序撒犀。您可以通過虛擬實現(xiàn)其方法來模擬此接口,并測試您的網(wǎng)絡(luò)協(xié)議是否運行正常掏秩。
net.Conn通過使用標(biāo)準(zhǔn)庫中的方法或舞,您可以準(zhǔn)備好使用真正的實現(xiàn):
net.Dial - 這個方法會返回我們可以用來與遠程服務(wù)器交談的連接對象
net.Listener.Accept() - 此方法將返回代表連接到服務(wù)器的客戶端的連接。方法Accept()的定義interface Listener和它的工作方式取決于這個接口的實現(xiàn)蒙幻。
http.ResponseWriter
當(dāng)我們使用HTTP連接時映凳,這個接口最常用。它用于將數(shù)據(jù)發(fā)送回客戶端邮破。它有一個簡單的定義:
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(int)
}
這三種方法都很容易記住語義:
-
Header()
- 它提供了設(shè)置自定義HTTP標(biāo)頭的功能:func handler(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/plain") }
-
Write()
- 將響應(yīng)主體發(fā)送給客戶端:func handler(w http.ResponseWriter, req *http.Request) { w.Write([]byte("Test")) }
-
WriteHeader()
- 設(shè)置HTTP響應(yīng)狀態(tài)碼(例如200
或404
):func handler(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) }
接口ResponseWriter
可以使用httptest.ResponseRecorder
struct [ doc ] 來模擬诈豌,這是它的一個實現(xiàn)。這樣抒和,在Golang中測試HTTP服務(wù)器非常簡單矫渔。
image.Image
該界面表示只讀圖像。您可以在給定的坐標(biāo)處讀取顏色數(shù)據(jù)摧莽。
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
這個界面非常簡單庙洼,有三種方法:
-
ColorModel()
- 返回有關(guān)圖像使用的顏色空間的信息(例如,RGBA) -
Bounds()
- 返回圖像尺寸數(shù)據(jù) -
At()
返回給定坐標(biāo)處的顏色信息
draw.Image
該界面代表可以修改的圖像。它將新方法添加到 image.Image
接口中油够。
type Image interface {
image.Image
Set(x, y int, c color.Color)
}
該Set()
方法可用于修改給定坐標(biāo)下的顏色數(shù)據(jù)蚁袭。
driver.Conn(SQL)[ doc ]
該接口用于各種SQL服務(wù)器連接實現(xiàn)。
type Conn interface {
Prepare(query string) (Stmt, error)
Close() error
Begin() (Tx, error)
}
大多數(shù)情況下石咬,您不需要使用此接口撕阎,因為它是為SQL驅(qū)動程序開發(fā)人員創(chuàng)建的。與Golang中的SQL服務(wù)器的正常連接將涉及為給定的SQL服務(wù)器類型(例如Postgresql碌补,MySQL)實現(xiàn)的sql.Open
函數(shù)和sql.BD
結(jié)構(gòu)driver.Conn
虏束。
sort.Interface [ doc ]
該接口用于定義比較數(shù)據(jù)類型的方法。
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
它有三種方法:
-
Len()
- 返回集合的大小 -
Less()
- 告訴給定索引中的一個元素是否比其他元素小 -
Swap()
- 用于在集合中的給定索引處交換元素
如果你希望你的集合可以通過標(biāo)準(zhǔn)的Golang函數(shù)進行排序厦章,你必須sort.Interface
為它創(chuàng)建適當(dāng)?shù)膶崿F(xiàn)镇匀。
結(jié)論
這篇文章列出了Golang中一些最重要的接口。當(dāng)然袜啃,這個列表并不完整汗侵,因為Go中有更多的接口。這篇文章中的內(nèi)容是一個很好的起點群发,并會告訴你你在處理什么痴脾,在大多數(shù)時間都很有用。