每個Go開發(fā)者都應(yīng)該知道的重要接口

原文: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)碼(例如200404):

    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ù)時間都很有用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末气筋,一起剝皮案震驚了整個濱河市慈俯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌起愈,老刑警劉巖只恨,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異抬虽,居然都是意外死亡官觅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門阐污,熙熙樓的掌柜王于貴愁眉苦臉地迎上來休涤,“玉大人,你說我怎么就攤上這事笛辟」Π保” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵隘膘,是天一觀的道長疑故。 經(jīng)常有香客問我,道長弯菊,這世上最難降的妖魔是什么纵势? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任踱阿,我火速辦了婚禮,結(jié)果婚禮上钦铁,老公的妹妹穿的比我還像新娘软舌。我一直安慰自己,他們只是感情好牛曹,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布佛点。 她就那樣靜靜地躺著,像睡著了一般黎比。 火紅的嫁衣襯著肌膚如雪超营。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天阅虫,我揣著相機與錄音演闭,去河邊找鬼。 笑死颓帝,一個胖子當(dāng)著我的面吹牛米碰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播购城,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吕座,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘪板?” 一聲冷哼從身側(cè)響起吴趴,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎篷帅,沒想到半個月后史侣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡魏身,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚪腐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箭昵。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖回季,靈堂內(nèi)的尸體忽然破棺而出家制,到底是詐尸還是另有隱情,我是刑警寧澤泡一,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布颤殴,位于F島的核電站,受9級特大地震影響鼻忠,放射性物質(zhì)發(fā)生泄漏涵但。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矮瘟。 院中可真熱鬧瞳脓,春花似錦、人聲如沸澈侠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哨啃。三九已至烧栋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拳球,已是汗流浹背劲弦。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留醇坝,地道東北人邑跪。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像呼猪,于是被迫代替她去往敵國和親画畅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理宋距,服務(wù)發(fā)現(xiàn)轴踱,斷路器,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • 01.{ 換行: Opening Brace Can't Be Placed on a Separate Lin...
    碼農(nóng)不器閱讀 2,402評論 0 14
  • fmt格式化字符串 格式:%[旗標(biāo)][寬度][.精度][arg索引]動詞旗標(biāo)有以下幾種:+: 對于數(shù)值類型總是輸出...
    皮皮v閱讀 1,099評論 0 3
  • 敬畏—進入—體驗—交給—持續(xù) 1,缺啥補啥谚赎,怕啥練啥淫僻; 2,一切為我所用,所用為團隊家壶唤; 3雳灵,我想變,我要變闸盔,我...
    GL_212a閱讀 127評論 0 0
  • 37-justoneheart-2018.01.22 1.和伴侶交流從來沒有用過“親愛的”這種昵稱悯辙,以前會覺得肉麻...
    Justoneheart閱讀 116評論 0 0