首頁 > 軟體

Go interface 介面的最佳實踐經驗分享

2022-04-15 13:01:01

Go語言-Go 介面的最佳實踐

原文連線:https://blog.boot.dev/golang/golang-interfaces/

Go 中的介面允許我們暫時將不同的型別視為相同的資料型別,因為這兩種型別實現相同的行為。它們是Go程式設計師工具箱的核心,並且經常被新的Go開發人員不正確地使用,導致程式碼不可讀且經常有錯誤。

什麼是Golang中的interface

In Go, an interface is a custom type that other types are able to implement, which gives Go developers a powerful way to use abstraction. Interfaces are named collections of method signatures, and when other types implement all the required methods, they implicitly implement the interface.

在 Go 中,介面是其他型別可以實現的自定義型別,這為 Go 開發人員提供了使用抽象的強大方式。介面是方法簽名的集合,當其他型別實現所有需要的方法時,它們隱式地實現了介面。

例如,Go 中的errors是介面,標準error介面很簡單,一個型別要被認為是error,所需要做的就是定義一個 Error ()方法,該方法不接受任何引數,並返回一個字串。

type error interface {
    Error() string
}

錯誤error的簡單性使得編寫紀錄檔和metrics 實現更加容易。讓我們定義一個表示網路問題的結構體:

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)
}

現在,我們可以在接受錯誤的任何地方使用 networkProblem struct 的範例。

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"

編寫介面的最佳實踐

編寫乾淨的介面是困難的。坦率地說,任何時候你都在處理程式碼中的抽象,如果你不小心的話,簡單可以很快變成複雜。讓我們回顧一下保持interfaces整潔的一些經驗法則。

  • Keep interfaces small 保持interfaces足夠小
  • Interfaces should have no knowledge of satisfying types 介面應該沒有令人滿意的型別的知識
  • Interfaces are not classes 介面不是類

1. 保持interfaces足夠小

If there is only one piece of advice that you take away from this article, make it this: keep interfaces small! Interfaces are meant to define the minimal behavior necessary to accurately represent an idea or concept.

如果您從本文中只得到了一條建議,那就是: 保持介面小一些!介面意味著定義精確表示一個想法或概念所需的最小行為。

下面是一個大型介面的標準 HTTP package的例子,它是定義最小行為的一個很好的例子:

type File interface {
    io.Closer
    io.Reader
    io.Seeker
    Readdir(count int) ([]os.FileInfo, error)
    Stat() (os.FileInfo, error)
}

Any type that satisfies the interface’s behaviors can be considered by the HTTP package as a File. This is convenient because the HTTP package doesn’t need to know if it’s dealing with a file on disk, a network buffer, or a simple []byte.

任何滿足介面行為的型別都可以被 HTTP package 視為File。這很方便,因為 HTTP package 不需要知道它是在處理磁碟上的檔案、還是網路緩衝區或是[]byte

2. Interfaces Should Have No Knowledge of Satisfying Types

An interface should define what is necessary for other types to classify as a member of that interface. They shouldn’t be aware of any types that happen to satisfy the interface at design time.

介面應該定義其他型別作為該介面的成員所必需的內容。他們不應該知道在設計時為了滿足介面而發生的任何型別。

例如,假設我們正在構建一個介面來描述定義汽車所必需的構成元素。

type car interface {
	Color() string
	Speed() int
	IsFiretruck() bool
}

Color() and Speed() make perfect sense, they are methods confined to the scope of a car. IsFiretruck() is an anti-pattern. We are forcing all cars to declare whether or not they are firetrucks. In order for this pattern to make any amount of sense, we would need a whole list of possible subtypes. IsPickup(), IsSedan(), IsTank()… where does it end??

Color()Speed()非常合理,它們是限制在汽車範圍內的方法。IsFiretruck ()是一個反模式。我們正在強制所有的汽車申報它們是否是消防車。為了使這個模式具有任何意義,我們需要一個可能的子型別的完整列表。IsPickup () ,IsSedan () ,IsTank () … 它在哪裡結束?

Instead, the developer should have relied on the native functionality of type assertion to derive the underlying type when given an instance of the car interface. Or, if a sub-interface is needed, it can be defined as:

相反,當給定汽車介面的範例時,開發人員應該依賴於型別斷言的原生功能來派生基礎型別。或者,如果需要子介面,可以將其定義為:

type firetruck interface {
	car
	HoseLength() int
}

它繼承了汽車所需的方法,並增加了一個額外的所需方法,使汽車一輛消防車。

3. 介面不是類

  • Interfaces are not classes, they are slimmer. 介面不是類,它們更小
  • Interfaces don’t have constructors or deconstructors that require that data is created or destroyed. 介面沒有要求建立或銷燬資料的建構函式或解構函數
  • Interfaces aren’t hierarchical by nature, though there is syntactic sugar to create interfaces that happen to be supersets of other interfaces. 介面本質上並不具有層次性,儘管在建立恰好是其他介面的超集的介面時存在語法糖
  • Interfaces define function signatures, but not underlying behavior. Making an interface often won’t 介面定義函數簽名,但不定義底層行為。製作interface通常不會在結構方法方面不干擾您的程式碼。例如,如果五種型別滿足錯誤介面,那麼它們都需要自己的版本的Error() function. 函數

有關介面的更多資訊

空的介面

空介面沒有指定任何方法,因此 Go 中的每個型別都實現了空介面。

interface{}

It’s for this reason that developers sometimes use a map[string]interface{} to work with arbitrary JSON data, although I recommend using anonymous structs instead where possible.

出於這個原因,開發人員有時使用 map[string]interface{}來處理任意 JSON 資料,儘管我推薦在可能的情況下使用匿名結構

Zero value of an interface

Interfaces can be nil, in fact, it’s their zero value. That’s why when we check for errors in Go, we’re always checking if err != nil, because err is an interface.

介面可以是 nil,事實上,這是它們的零值。這就是為什麼當我們在 Go 中檢查錯誤時,我們總是檢查err != nil,因為 err 是一個介面。

指標上的介面

It’s a common “gotcha” in Go to implement a method on a pointer type and expect the underlying type to implement the interface, it doesn’t work like that.

在 Go 中,在指標型別上實現一個方法並期望底層型別實現介面是一個常見的“明白了”,它不是這樣工作的。

type rectangle interface {
    height() int
    width() int
}

type square struct {
    length int
}

func (sq *square) width() int {
    return sq.length
}

func (sq *square) height() int {
    return sq.length
}

Though you may expect it to, in this example the square type does not implement the rectangle interface. The *square type does. If I wanted the square type to implement the rectangle interface I would just need to remove the pointer receivers.

雖然您可能希望這樣做,但是在這個範例中,正方形型別不實現矩形介面。它使用的是*square。如果我想讓正方形型別實現矩形介面,我只需要刪除指標接收器。

type rectangle interface {
    height() int
    width() int
}

type square struct {
    length int
}

func (sq square) width() int {
    return sq.length
}

func (sq square) height() int {
    return sq.length
}

到此這篇關於Go interface 介面的最佳實踐的文章就介紹到這了,更多相關Go interface 介面內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com