Golang接口型函數(shù)使用小結
什么是接口型函數(shù)?顧名思義接口函數(shù)指的是用函數(shù)實現(xiàn)接口,這樣在調用的時候就會非常簡便,這種方式適用于只有一個函數(shù)的接口。
這里以迭代一個map為例,演示這一實現(xiàn)的技巧。
常規(guī)接口實現(xiàn)
defer語句用于延遲函數(shù)調用,每次會把一個函數(shù)壓入棧中,函數(shù)返回前再把延遲的函數(shù)取出并執(zhí)行。延遲函數(shù)可以有參數(shù):
- 延遲函數(shù)的參數(shù)在defer語句出現(xiàn)時就已確定下來(傳值的就是當前值);
- 延遲函數(shù)執(zhí)行按后進先出順序執(zhí)行;
- 延遲函數(shù)可操作主函數(shù)的具名返回值(修改返回值);
type Handler interface { DoFunc(k, v interface{}) } func DoEach(m map[interface{}]interface{}, h Handler) { if m != nil && len(m) > 0 { for k, v := range m { h.DoFunc(k, v) } } }
這里我們定義了一個Handler接口,只有一個DoFunc方法,接收k,v兩個參數(shù),這就是一個接口了,我們后面會實現(xiàn)他,具體做什么由我們的實現(xiàn)決定。
然后我們定義了一個DoEach函數(shù),該函數(shù)的功能就是迭代傳遞過來的map參數(shù),然后把map的每個key和value值傳遞給Handler的DoFunc方法,
具體由這個Handler的實現(xiàn)來決定,這也是面向接口編程。
說得再多不如來點實際的例子:用我們剛剛定義的DoEach方法和Handler接口。
package main import "fmt" type Handler interface { DoFunc(k, v interface{}) } func DoEach(m map[interface{}]interface{}, h Handler) { if m != nil && len(m) > 0 { for k, v := range m { h.DoFunc(k, v) } } } type greet string func ( g greet) DoFunc(k, v interface{}) { fmt.Printf("%s,在下%s,我的必殺技是%s\n", g, k, v) } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" var g greet = "諸位英雄" DoEach(persons, g) }
輸出:
諸位英雄,在下喬峰,我的必殺技是龍爪手
諸位英雄,在下鳩摩智,我的必殺技是小無相功
諸位英雄,在下慕容復,我的必殺技是斗轉星移
以上實現(xiàn),我們定義了一個map來存儲幾位大佬,map的key是大佬的名字,value是該大佬的絕技。greet是我們新定義的類型,
其對應基本類型string,該greet實現(xiàn)了Handler接口,打印出自我介紹的信息。
接口型函數(shù)出場
關于上面的實現(xiàn),我們可以發(fā)現(xiàn),有兩點不太好:
- 因為必須要實現(xiàn)Handler接口,DoFunc這個方法名不能修改,不能定義一個更有意義的名字
- 必須要新定義一個類型,才可以實現(xiàn)Handler接口,才能使用DoEach函數(shù)
首先我們先解決第一個問題,根據(jù)我們具體做的事情定義一個更有意義的方法名,比如例子中是自我介紹,
那么我們使用selfintroduction是不是要比DoFunc這個語義的方法要好呢。
如果調用者改了方法名,那么就不能實現(xiàn)Handler接口,還要使用DoEach方法怎么辦?那就是由提供DoEach函數(shù)的負責提供Handler的實現(xiàn),
我們改造下代碼如下:
type HandlerFunc func(k, v interface{}) func (f HandlerFunc) DoFunc(k, v interface{}) { f(k, v) }
上面代碼我們定義了一個新的類型HandlerFunc,它是一個func(k, v interface{})類型,然后這個新的HandlerFunc實現(xiàn)了Handler接口(原始實現(xiàn)方式中的
type Handler interface { DoFunc(k, v interface{}) }
),DoFunc方法的實現(xiàn)是調用HandlerFunc本身,因為HandlerFunc類型的變量就是一個方法?,F(xiàn)在我們使用這種方式實現(xiàn)同樣的效果。
完整代碼如下:
package main import "fmt" type Handler interface { DoFunc(k, v interface{}) } type HandlerFunc func(k, v interface{}) func (f HandlerFunc) DoFunc(k, v interface{}) { f(k, v) } type greet string func (g greet) selfintroduction(k, v interface{}) { fmt.Printf("%s,在下%s,我的必殺技是%s\n", g, k, v) } func DoEach(m map[interface{}]interface{}, h Handler) { if m != nil && len(m) > 0 { for k, v := range m { h.DoFunc(k, v) } } } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" var g greet = "諸位英雄" DoEach(persons, HandlerFunc(g.selfintroduction)) }
輸出:
諸位英雄,在下喬峰,我的必殺技是龍爪手
諸位英雄,在下鳩摩智,我的必殺技是小無相功
諸位英雄,在下慕容復,我的必殺技是斗轉星移
還是差不多原來的實現(xiàn),只是把原接口方法名DoFunc改為selfintroduction。HandlerFunc(g.selfintroduction)不是方法的調用,而是轉型,因為selfintroduction和HandlerFunc是同一種類型,
所以可以強制轉型。轉型后,因為HandlerFunc實現(xiàn)了Handler接口,所以我們就可以繼續(xù)使用原來的DoEach方法了。
進一步改造
現(xiàn)在解決了命名的問題,但是每次強制轉型是不是不太好?我們繼續(xù)重構下,可以采用新定義一個函數(shù)的方式,幫助調用者強制轉型。
完整代碼如下:
package main import "fmt" type Handler interface { DoFunc(k, v interface{}) } type HandlerFunc func(k, v interface{}) func (f HandlerFunc) DoFunc(k, v interface{}) { f(k, v) } type greet string func (g greet) selfintroduction(k, v interface{}) { fmt.Printf("%s,在下%s,我的必殺技是%s\n", g, k, v) } func DoEach(m map[interface{}]interface{}, h Handler) { if m != nil && len(m) > 0 { for k, v := range m { h.DoFunc(k, v) } } } func EachFunc(m map[interface{}]interface{}, f func(k, v interface{})) { DoEach(m, HandlerFunc(f)) } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" var g greet = "諸位英雄" EachFunc(persons, g.selfintroduction) }
上面我們新增了一個EachFunc函數(shù),幫助調用者強制轉型,調用者就不用自己做了。
現(xiàn)在我們發(fā)現(xiàn)EachFunc函數(shù)接收的是一個func(k, v interface{})類型的函數(shù),沒有必要實現(xiàn)原Handler接口了,所以我們新的類型可以去掉不用了。
去掉了自定義類型greet之后,整個代碼更簡潔,可讀性是不是更好點?簡潔干凈的完整代碼如下:
package main import "fmt" type Handler interface { DoFunc(k, v interface{}) } type HandlerFunc func(k, v interface{}) func (f HandlerFunc) DoFunc(k, v interface{}) { f(k, v) } func DoEach(m map[interface{}]interface{}, h Handler) { if m != nil && len(m) > 0 { for k, v := range m { h.DoFunc(k, v) } } } func EachFunc(m map[interface{}]interface{}, f func(k, v interface{})) { DoEach(m, HandlerFunc(f)) } func selfintroduction(k, v interface{}) { fmt.Printf("諸位英雄,在下%s,我的必殺技是%s\n", k, v) } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" EachFunc(persons, selfintroduction) }
以上關于函數(shù)型接口就寫完了,如果大家仔細留意,發(fā)現(xiàn)和我們自己平時使用的http.Handle方法非常類似,其實接口http.Handler就是這么實現(xiàn)的。
type Handler interface { ServeHTTP(ResponseWriter, *Request) } func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
這是一種非常好的技巧,提供兩種函數(shù),既可以以接口的方式使用,也可以以方法的方式,
對應我們例子中的DoEach和EachFunc這兩個函數(shù),靈活方便,也更符合自然語言規(guī)則吧。
無論從事什么行業(yè),只要做好兩件事就夠了,一個是你的專業(yè)、一個是你的人品,專業(yè)決定了你的存在,人品決定了你的人脈,剩下的就是堅持,用善良專業(yè)和真誠贏取更多的信任。
到此這篇關于Golang接口型函數(shù)使用技巧的文章就介紹到這了,更多相關Golang接口使用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Golang 語言控制并發(fā) Goroutine的方法
本文我們介紹了不同場景中分別適合哪種控制并發(fā) goroutine 的方式,其中,channel 適合控制少量 并發(fā) goroutine,WaitGroup 適合控制一組并發(fā) goroutine,而 context 適合控制多級并發(fā) goroutine,感興趣的朋友跟隨小編一起看看吧2021-06-06解決Go語言中高頻次和高并發(fā)下隨機數(shù)重復的問題
在Golang中,獲取隨機數(shù)的方法一般會介紹有兩種,一種是基于math/rand的偽隨機,一種是基于crypto/rand的真隨機,math/rand由于其偽隨機的原理,經(jīng)常會出現(xiàn)重復的隨機數(shù),導致在需要進行隨機的業(yè)務出現(xiàn)較多的重復問題,所以本文給大家介紹了較好的解放方案2023-12-12