日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

六個常見的 Go 接口設(shè)計錯誤

來源: 責(zé)編: 時間:2024-07-03 17:19:53 179觀看
導(dǎo)讀Go 是一門新興的語言,如果你正在使用它,很可能這不是你的第一門編程語言。從不同的語言背景轉(zhuǎn)來,你帶來了自己的既有經(jīng)驗和范式。你在以前的語言中習(xí)慣做的事情,在 Go 中可能并不是一個好的方式。學(xué)習(xí) Go 不僅僅是學(xué)習(xí)一

Go 是一門新興的語言,如果你正在使用它,很可能這不是你的第一門編程語言。tII28資訊網(wǎng)——每日最新資訊28at.com

從不同的語言背景轉(zhuǎn)來,你帶來了自己的既有經(jīng)驗和范式。你在以前的語言中習(xí)慣做的事情,在 Go 中可能并不是一個好的方式。tII28資訊網(wǎng)——每日最新資訊28at.com

學(xué)習(xí) Go 不僅僅是學(xué)習(xí)一種新的語法。它還涉及到學(xué)習(xí)一種新的程序思維方式。tII28資訊網(wǎng)——每日最新資訊28at.com

一些理論知識

Go 創(chuàng)始人 Rob Pike 曾經(jīng)說過:“Go 的接口并不是 Java 或 C# 接口的變種,而是更多。它們是大規(guī)模編程和適應(yīng)性強的進化設(shè)計關(guān)鍵?!?span style="display:none">tII28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片tII28資訊網(wǎng)——每日最新資訊28at.com

正確使用接口可以帶來簡單性、可讀性和更靈活的代碼設(shè)計。(不正確使用會帶來新的災(zāi)難)tII28資訊網(wǎng)——每日最新資訊28at.com

以下是建議了解的基本原則 TOP3:tII28資訊網(wǎng)——每日最新資訊28at.com

  • 接口隔離原則:客戶端永遠不應(yīng)該被迫實現(xiàn)它不使用的接口,或者客戶端不應(yīng)該被迫依賴它們不使用的方法。
  • 多態(tài)性:一段代碼根據(jù)它接收到的具體數(shù)據(jù)改變其行為。
  • 里氏替換原則:如果你的代碼依賴于一個抽象,一個實現(xiàn)可以被另一個實現(xiàn)替換,而不需要改變你的代碼。

常見接口設(shè)計錯誤

1. 你創(chuàng)建了太多的接口

擁有過多接口的情況叫做:接口污染。當(dāng)你在編寫具體類型之前就開始抽象時,就會出現(xiàn)這種情況。tII28資訊網(wǎng)——每日最新資訊28at.com

由于無法預(yù)知需要哪些抽象,因此很容易編寫出過多的接口,而這些接口在日后要么是錯誤的,要么是無用的。tII28資訊網(wǎng)——每日最新資訊28at.com

Go 創(chuàng)始人給出了一個很棒的指導(dǎo)方針幫助我們避免接口污染。如下:tII28資訊網(wǎng)——每日最新資訊28at.com

不要使用接口設(shè)計,而是發(fā)現(xiàn)它們(Don’t design with interfaces, discover them.) —— Rob PiketII28資訊網(wǎng)——每日最新資訊28at.com

Rob 在這里指出的是:你不需要提前考慮你需要什么抽象。你可以從具體的結(jié)構(gòu)體開始設(shè)計,并在設(shè)計需要時只創(chuàng)建接口。通過這樣做,你的代碼會有機地增長到預(yù)期的設(shè)計。tII28資訊網(wǎng)——每日最新資訊28at.com

但我仍然看到人們提前創(chuàng)建接口,因為他們認為他們將來可能需要多個實現(xiàn)。tII28資訊網(wǎng)——每日最新資訊28at.com

對于他們,我要說:tII28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片tII28資訊網(wǎng)——每日最新資訊28at.com

懶惰但要懶惰得好。創(chuàng)建接口的完美時機是當(dāng)你真正需要它的時候,而不是當(dāng)你預(yù)測你需要它的時候。tII28資訊網(wǎng)——每日最新資訊28at.com

無用的接口往往只有一個實現(xiàn)。它們只是增加了一個額外的間接層,迫使程序員在他們實際上想要去實現(xiàn)時總是要通過這一層。tII28資訊網(wǎng)——每日最新資訊28at.com

一個接口有一個成本:它是你在推理你的代碼時需要記住的一個新概念。正如 Djikstra 所說,一個理想的接口必須是:“一個可以絕對精確的新語義層面?!?span style="display:none">tII28資訊網(wǎng)——每日最新資訊28at.com

所以在創(chuàng)建接口之前問問自己:你有多個接口的實現(xiàn)嗎?我強調(diào)了 “有”,因為 “將有” 意味著你可以預(yù)測未來,而你不能。tII28資訊網(wǎng)——每日最新資訊28at.com

2. 你的方法太多了

在 PHP 項目中看到 10 個方法的接口是很典型的。在 Go 中,接口很小,標準庫中所有接口的平均方法數(shù)是 2。tII28資訊網(wǎng)——每日最新資訊28at.com

“越大的接口抽象就越弱”,這是 Go 諺語之一。正如 Rob Pike 所說,這是關(guān)于接口最重要的事情,這意味著接口越小,它就越有用。tII28資訊網(wǎng)——每日最新資訊28at.com

一個接口可以有的實現(xiàn)越多,它就越通用。如果你有一個包含大量方法的接口,就很難有多個實現(xiàn)。tII28資訊網(wǎng)——每日最新資訊28at.com

你擁有的方法越多,接口就越具體。接口越具體,不同類型的機會就越小,它們可以顯示相同的行為。tII28資訊網(wǎng)——每日最新資訊28at.com

io.Reader 和 io.Writer 就是有用接口的一個很好的例子,它們有數(shù)以百計的實現(xiàn)?;蛘呤亲罱?jīng)典的 error 接口,它非常強大,可以在 Go 中實現(xiàn)整個錯誤處理。tII28資訊網(wǎng)——每日最新資訊28at.com

記住,你可以從其他接口組合一個接口。這里有一個例子:ReadWriteCloser 由 3 個較小的接口組成。tII28資訊網(wǎng)——每日最新資訊28at.com

代碼如下:tII28資訊網(wǎng)——每日最新資訊28at.com

type ReadWriteCloser interface {    io.Reader    io.Writer    io.Closer}

3. 你沒有編寫行為驅(qū)動的接口

在傳統(tǒng)語言中,諸如 User、Request 等名詞性接口非常常見。而在 Go 語言中,大多數(shù)接口都有后綴:Reader、Writer、Closer 等。這是因為,在 Go 中,接口暴露了行為,而它們的名稱則指向該行為。tII28資訊網(wǎng)——每日最新資訊28at.com

在 Go 中定義接口時,你定義的不是 "某物是什么",而是 "它提供了什么"-- 是 "行為",而不是 "事物"!tII28資訊網(wǎng)——每日最新資訊28at.com

這就是為什么 Go 中沒有 File 接口,但有Reader 和 Writer:這些都是行為,而 File 是實現(xiàn) Reader 和 Writer 的事物。tII28資訊網(wǎng)——每日最新資訊28at.com

《Effective Go》也提到了同樣的觀點:tII28資訊網(wǎng)——每日最新資訊28at.com

Go 中的接口提供了一種指定對象行為的方法:如果某個東西可以做到這一點,那么它就可以用在這里。tII28資訊網(wǎng)——每日最新資訊28at.com

在編寫接口時,盡量考慮動作或行為。如果你定義了一個名為 "Thing" 的接口,問問自己為什么這個 "Thing" 不是一個結(jié)構(gòu)體。tII28資訊網(wǎng)——每日最新資訊28at.com

4. 你在生產(chǎn)者端編寫接口

我經(jīng)常在代碼審查中看到這種情況:很多開發(fā)者在同一個包,既寫了具體的實現(xiàn),又定義了接口。tII28資訊網(wǎng)——每日最新資訊28at.com

生產(chǎn)者定義的接口生產(chǎn)者定義的接口tII28資訊網(wǎng)——每日最新資訊28at.com

但是,也許客戶端并不想使用生產(chǎn)者接口中的所有方法。請記住 "接口隔離原則" 中的一句話:"不應(yīng)強迫客戶端實現(xiàn)其不使用的方法"。tII28資訊網(wǎng)——每日最新資訊28at.com

下面是一個例子:tII28資訊網(wǎng)——每日最新資訊28at.com

package main// ====== producer side// This interface is not neededtype UsersRepository interface {    GetAllUsers()    GetUser(id string)}type UserRepository struct {}func (UserRepository) GetAllUsers()      {}func (UserRepository) GetUser(id string) {}// ====== client side// Client only needs GetUser and// can create this interface implicitly implemented// by concrete UserRepository on his sidetype UserGetter interface {    GetUser(id string)}

如果客戶端想使用生產(chǎn)者的所有方法,可以使用具體的結(jié)構(gòu)體。因為結(jié)構(gòu)體方法已經(jīng)提供了這些行為。tII28資訊網(wǎng)——每日最新資訊28at.com

即使客戶端想要解耦代碼并使用多種實現(xiàn),他也可以在自己這邊創(chuàng)建一個包含所有方法的接口:tII28資訊網(wǎng)——每日最新資訊28at.com

在客戶端定義的接口在客戶端定義的接口tII28資訊網(wǎng)——每日最新資訊28at.com

由于 Go 中的接口是隱式滿足的,所以這些事情才得以實現(xiàn)??蛻舳舜a不再需要導(dǎo)入某個接口并編寫實現(xiàn),因為 Go 中沒有這樣的關(guān)鍵字。tII28資訊網(wǎng)——每日最新資訊28at.com

如果實現(xiàn)與接口有相同的方法,那么實現(xiàn)就已經(jīng)滿足了該接口,可以在客戶端代碼中使用。tII28資訊網(wǎng)——每日最新資訊28at.com

5. 你正在返回接口

如果一個方法返回的是接口而不是具體的結(jié)構(gòu),那么調(diào)用該方法的所有客戶端都將被迫使用相同的抽象。tII28資訊網(wǎng)——每日最新資訊28at.com

你需要讓客戶端決定他們需要什么樣的抽象,因為代碼是他們的庭院。tII28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)你想使用結(jié)構(gòu)體中的某項功能時,卻因為接口沒有公開它而無法使用,這是很煩人的。這種限制可能是有原因的,但并非總是如此。tII28資訊網(wǎng)——每日最新資訊28at.com

下面是一個人為的例子:tII28資訊網(wǎng)——每日最新資訊28at.com

type Shape interface {    Area() float64    Perimeter() float64}type Circle struct {    Radius float64}func (c Circle) Area() float64 {    return math.Pi * c.Radius * c.Radius}func (c Circle) Perimeter() float64 {    return 2 * math.Pi * c.Radius}// NewCircle returns an interface instead of structfunc NewCircle(radius float64) Shape {    return Circle{Radius: radius}}func main() {    circle := NewCircle(5)    // we lose access to circle.Radius}

在上面的示例中,我們不僅無法訪問 circle.Radius,而且每次要訪問它時都需要在代碼中添加類型斷言:tII28資訊網(wǎng)——每日最新資訊28at.com

shape := NewCircle(5)if circle, ok := shape.(Circle); ok {    fmt.Println(circle.Radius)}

因此在設(shè)計上,請遵循 Postel 定律:“發(fā)送時要保守,接受時要寬松”,從方法中返回具體的結(jié)構(gòu),并選擇接受接口。tII28資訊網(wǎng)——每日最新資訊28at.com

結(jié)合現(xiàn)實的代碼,例如原本的代碼是:tII28資訊網(wǎng)——每日最新資訊28at.com

// Save writes the contents of doc to the file f.func Save(f *os.File, doc *Document) error

但入?yún)⑹?nbsp;f *os.File,這并不靈活,也不便于測試。我們可以通過上面提到的接口方式,改造為如下代碼:tII28資訊網(wǎng)——每日最新資訊28at.com

// Save writes the contents of doc to the supplied// Writer.func Save(w io.Writer, doc *Document) error

6. 你沒有驗證接口合規(guī)性

假設(shè)一個場景,你有一個導(dǎo)出名為 User 的類型的軟件包,你實現(xiàn)了 Stringer 接口。tII28資訊網(wǎng)——每日最新資訊28at.com

因為出于某種業(yè)務(wù)原因,當(dāng)你打印時,你不希望顯示 email 字段。需要如此實現(xiàn)邏輯:tII28資訊網(wǎng)——每日最新資訊28at.com

package userstype User struct {    Name  string    Email string}func (u User) String() string {    return u.Name}

客戶端的代碼如下:tII28資訊網(wǎng)——每日最新資訊28at.com

package mainimport (    "fmt"    "pkg/users")func main() {    u := users.User{       Name:  "腦子進煎魚了",       Email: "xxx@gmail.com",    }    fmt.Printf("%s", u)}

這將正確輸出:腦子進煎魚了。tII28資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在,假設(shè)你進行了重構(gòu),不小心刪除或注釋了 String() 的實現(xiàn),你的代碼看起來就像這樣:tII28資訊網(wǎng)——每日最新資訊28at.com

package userstype User struct {    Name  string    Email string}

在這種情況下,您的代碼仍然可以編譯和運行,但輸出結(jié)果將是 {腦子進煎魚了 xxx@gmail.com}。沒有任何程序反饋來提示你出現(xiàn)了問題。編譯器也不會報錯。tII28資訊網(wǎng)——每日最新資訊28at.com

這種場景下,為了強制校驗?zāi)硞€類型是否實現(xiàn)了某個接口,我們可以這樣做:tII28資訊網(wǎng)——每日最新資訊28at.com

package usersimport "fmt"type User struct {    Name  string    Email string}var _ fmt.Stringer = User{} // User implements the fmt.Stringerfunc (u User) String() string {    return u.Name}

我們再嘗試一次?,F(xiàn)在如果我們刪除 String() 方法,就會在構(gòu)建時得到如下結(jié)果:tII28資訊網(wǎng)——每日最新資訊28at.com

cannot use User{} (value of type User) as fmt.Stringer value in variable declaration: User does not implement fmt.Stringer (missing method String)

在該行中,我們試圖將一個空的 User{} 賦值給一個 fmt.Stringer類型的變量。由于 User{} 不再實現(xiàn) fmt.Stringer,我們得到了程序的報錯反饋。tII28資訊網(wǎng)——每日最新資訊28at.com

我們在變量名中使用了 _,因為我們并沒有真正使用它,所以不會執(zhí)行真正的分配。tII28資訊網(wǎng)——每日最新資訊28at.com

上面我們看到 User 實現(xiàn)了接口。User 和 *User 是不同的類型。因此,如果你想讓 *User 實現(xiàn)它,你可以這樣實現(xiàn):tII28資訊網(wǎng)——每日最新資訊28at.com

var _ fmt.Stringer = (*User)(nil) // *User implements the fmt.Stringer

并且通過這種實現(xiàn),IDE 會顯式提示我們的方法是否有缺失。少了的話會有報錯提示:tII28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片tII28資訊網(wǎng)——每日最新資訊28at.com

還是非常方便的。當(dāng)然,這個小技巧,不需要對每個實現(xiàn)接口的類型都這樣做,根據(jù)需求對于有必要強校驗的接口即可。tII28資訊網(wǎng)——每日最新資訊28at.com

總結(jié)

今天這篇文章給大家介紹了六種常見的接口設(shè)計錯誤:創(chuàng)建了太多的接口、方法太多了、沒有編寫行為驅(qū)動的接口、在生產(chǎn)者端編寫接口、正在返回接口、沒有驗證接口合規(guī)性。tII28資訊網(wǎng)——每日最新資訊28at.com

我還記得以前看到某個項目里,寫了個接口,20~30 個有待實現(xiàn)的方法。然而他就真的只是一個接口,那么多年過去了也沒有人再去實現(xiàn)這個接口。tII28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)然,本文提供的很多接口優(yōu)化建議,也是需要結(jié)合你的實際代碼(業(yè)務(wù))場景去考慮的。大家各取所需,學(xué)習(xí)之即可。tII28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-98556-0.html六個常見的 Go 接口設(shè)計錯誤

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 秒殺系統(tǒng)架構(gòu)解析:應(yīng)對高并發(fā)的藝術(shù)

下一篇: CSS 實現(xiàn)3d輪播圖的一些思路,你學(xué)會了嗎?

標簽:
  • 熱門焦點
  • 影音體驗是真的強 簡單聊聊iQOO Pad

    大公司的好處就是產(chǎn)品線豐富,非常細分化的東西也能給你做出來,例如早先我們看到了新的vivo Pad2,之后我們又在iQOO Neo8 Pro的發(fā)布會上看到了iQOO的首款平板產(chǎn)品iQOO Pad。雖
  • 線程通訊的三種方法!通俗易懂

    線程通信是指多個線程之間通過某種機制進行協(xié)調(diào)和交互,例如,線程等待和通知機制就是線程通訊的主要手段之一。 在 Java 中,線程等待和通知的實現(xiàn)手段有以下幾種方式:Object 類下
  • 只需五步,使用start.spring.io快速入門Spring編程

    步驟1打開https://start.spring.io/,按照屏幕截圖中的內(nèi)容創(chuàng)建項目,添加 Spring Web 依賴項,并單擊“生成”按鈕下載 .zip 文件,為下一步做準備。請在進入步驟2之前進行解壓。圖
  • 一篇文章帶你了解 CSS 屬性選擇器

    屬性選擇器對帶有指定屬性的 HTML 元素設(shè)置樣式??梢詾閾碛兄付▽傩缘?HTML 元素設(shè)置樣式,而不僅限于 class 和 id 屬性。一、了解屬性選擇器CSS屬性選擇器提供了一種簡單而
  • 梁柱接棒兩年,騰訊音樂闖出新路子

    文丨田靜 出品丨牛刀財經(jīng)(niudaocaijing)7月5日,企鵝FM發(fā)布官方公告稱由于業(yè)務(wù)調(diào)整,將于9月6日正式停止運營,這意味著騰訊音樂長音頻業(yè)務(wù)走向消亡。騰訊在長音頻領(lǐng)域還在摸索。為
  • 三星Galaxy Z Fold5今日亮相:厚度縮減但仍略顯厚重

    據(jù)官方此前宣布,三星將于7月26日也就是今天在韓國首爾舉辦Unpacked活動,屆時將帶來帶來包括Galaxy Buds 3、Galaxy Watch 6、Galaxy Tab S9、Galaxy
  • 7月4日見!iQOO 11S官宣:“雞血版”驍龍8 Gen2+200W快充加持

    上半年已接近尾聲,截至目前各大品牌旗下的頂級旗艦都已悉數(shù)亮相,而下半年即將推出的頂級旗艦已經(jīng)成為了數(shù)碼圈爆料的主流,其中就包括全新的iQOO 11S系
  • iQOO Neo8 Pro搶先上架:首發(fā)天璣9200+ 安卓性能之王

    經(jīng)過了一段時間的密集爆料,昨日iQOO官方如期對外宣布:將于5月23日推出全新的iQOO Neo8系列新品,官方稱這是一款擁有旗艦級性能調(diào)校的作品。隨著發(fā)布時
  • 利用職權(quán)私自解除被封帳號 Meta開除20多名員工

    11月18日消息,據(jù)外媒援引知情人士表示,過去一年時間內(nèi),F(xiàn)acebook母公司Meta解雇或處罰了20多名員工以及合同工,指控這些人通過內(nèi)部系統(tǒng)以不當(dāng)方式重置用戶帳號,其
Top 主站蜘蛛池模板: 镇宁| 襄汾县| 武安市| 游戏| 镇原县| 盐源县| 蓝山县| 疏附县| 达孜县| 昔阳县| 瓮安县| 定襄县| 广西| 崇仁县| 安泽县| 晋江市| 乌兰察布市| 梅州市| 张家川| 嵊泗县| 姚安县| 五寨县| 秀山| 涞源县| 连南| 安塞县| 大荔县| 当阳市| 德格县| 万州区| 西昌市| 全南县| 曲阜市| 长沙市| 姜堰市| 时尚| 云龙县| 上饶市| 白朗县| 克东县| 平罗县|