Golang 語言怎麼使用接口編程?
介紹
關於 Golang 語言接口的使用,在之前的一篇公衆號文章中已經介紹過,未閱讀的讀者朋友,如果感興趣,可以按需翻閱文末推薦閱讀列表。本文我們主要介紹在 Golang 語言中,如何使用接口編程?以及接口的使用技巧。
接口編程
在 Golang 應用開發中,除了使用 Func,我們還經常會用到 Method,比如:
示例代碼:
type Cat struct {
name string
}
func (c Cat) Eat() {
fmt.Printf("%s 正在喫飯\n", c.name)
}
func main () {
c := Cat{name: "kitty"}
c.Eat()
}
閱讀上面這段代碼,我們定義了一個 Cat 結構體,然後實現 Eat 方法。在 Golang 語言中,使用 Method 和使用 Func 的區別是,使用 Method 可以將類型和方法封裝在一起,實現強耦合。
但是,如果我們除了 Cat 之外,現在又新增了 Dog,也要實現 Eat 方法。我們除了也定義一個 Dog 結構體,然後實現 Eat 方法之外。還可以定義一個 Animal 接口,實現多態。
示例代碼:
type Animal interface {
Eat()
}
type Cat struct {
name string
}
type Dog struct {
name string
}
func (c Cat) Eat() {
fmt.Printf("%s 正在喫飯\n", c.name)
}
func (c Cat) Sleep() {
fmt.Printf("%s 正在睡覺\n", c.name)
}
func (d Dog) Eat() {
fmt.Printf("%s 正在喫飯\n", d.name)
}
func main () {
var a Animal
c := Cat{name: "kitty"}
d := Dog{name: "101"}
a = c
a.Eat()
a = d
a.Eat()
}
閱讀上面這段代碼,我們定義了一個包含 Eat()
方法的接口 Animal,Cat 和 Dog 分別實現 Animal 接口的 Eat()
方法,然後就可以通過將 Cat 類型和 Dog 類型的變量賦值給 Animal 接口,實現多態。
除此之外,我們還可以對上面這段代碼進一步優化,上面這段代碼雖然實現了多態,但是實現上有些繁瑣。我們可以聲明一個接收 Animal 接口類型參數的函數 AnimalAction()
。
示例代碼:
type Animal interface {
Action() string
}
type Cat struct {
name string
}
type Dog struct {
name string
}
func (c Cat) Action() string {
return fmt.Sprintf("Cat %s 正在喫飯", c.name)
}
func (d Dog) Action() string {
return fmt.Sprintf("Dog %s 正在喫飯", d.name)
}
func AnimalAction (a Animal) {
fmt.Println(a.Action())
}
func main () {
c := Cat{name: "Kitty"}
AnimalAction(c)
d := Dog{name: "101"}
AnimalAction(d)
}
閱讀上面這段代碼,是否感覺似曾相識。在 Golang 語言標準庫中有很多這種用法。
接口使用技巧
-
儘量定義包含方法少的接口,建議控制接口方法數量不超過 3 個
我們可以在一些 Golang 語言標準庫中發現,很多接口包含的方法數量都不超過 3 個,也有很多接口僅包含 1 個方法。
控制接口包含方法的數量儘量少的好處是接口包含的方法越少,越容易實現和組合。
-
如何強制實現接口的所有方法
Golang 語言中的接口是隱式實現的,並且不強制實現接口的所有方法。如果我們需要強制實現接口的所有方法,做法如下:
示例代碼:
type Animal interface { Eat() Sleep() } type Cat struct {} func (c Cat) Eat() { fmt.Println("Cat 正在喫飯") } // func (c Cat) Sleep() { // fmt.Println("Cat 正在睡覺") // } type Dog struct {} func (d Dog) Eat() { fmt.Println("Dog 正在喫飯") } // func (d Dog) Sleep() { // fmt.Println("Dog 正在睡覺") // } func main () { var _ Animal = (*Cat)(nil) var _ Animal = (*Dog)(nil) c := Cat{} c.Eat() d := Dog{} d.Eat() }
OutPut:
cannot use (*Cat)(nil) (type *Cat) as type Animal in assignment: *Cat does not implement Animal (missing Sleep method) cannot use (*Dog)(nil) (type *Dog) as type Animal in assignment: *Dog does not implement Animal (missing Sleep method)
閱讀上面這段代碼,我們通過聲明變量:
var _ Animal = (*Cat)(nil) var _ Animal = (*Dog)(nil)
強制實現接口的所有方法。
-
儘量不使用空接口類型作爲函數參數
Golang 語言是強類型靜態語言,Golang 編譯器在編譯期間會對變量做類型檢查。如果函數或方法接收的參數類型是空接口
interface{}
,編譯器將收不到任何信息,也就不會對空接口類型的變量進行類型檢查,接收參數的類型將需要開發者自己做類型檢查。所以開發者儘量不要使用空接口interface{}
變量作爲接收參數。但是空接口
interface{}
類型也並非完全無用武之地,因爲目前 Golang 語言(v1.16.4)還未支持泛型,當需要處理未知類型的參數時,可以使用空接口interface{}
類型,在 Golang 語言標準庫中也有該使用方式,比如fmt
包。
總結
本文我們介紹瞭如何使用接口編程,通過一個簡單示例,循序漸進地介紹了接口編程的使用方式,此外,我們還介紹了一些接口使用技巧。
建議讀者朋友們動手敲一下示例代碼,通過親自運行代碼加深理解。關於接口本身的介紹,讀者朋友們可以按需閱讀推薦列表中的相關文章。
參考資料:
https://golang.org/doc/effective_go#interfaces_and_types
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/YunhA72jyar9g0sFZ6rg8A