<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
什麼是介面型函數?顧名思義介面函數指的是用函數實現介面,這樣在呼叫的時候就會非常簡便,這種方式適用於只有一個函數的介面。
這裡以迭代一個map為例,演示這一實現的技巧。
defer語句用於延遲函數呼叫,每次會把一個函數壓入棧中,函數返回前再把延遲的函數取出並執行。延遲函數可以有引數:
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兩個引數,這就是一個介面了,我們後面會實現他,具體做什麼由我們的實現決定。
然後我們定義了一個DoEach函數,該函數的功能就是迭代傳遞過來的map引數,然後把map的每個key和value值傳遞給Handler的DoFunc方法,
具體由這個Handler的實現來決定,這也是面向介面程式設計。
說得再多不如來點實際的例子:用我們剛剛定義的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,我的必殺技是%sn", g, k, v) } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" var g greet = "諸位英雄" DoEach(persons, g) }
輸出:
諸位英雄,在下喬峰,我的必殺技是龍爪手
諸位英雄,在下鳩摩智,我的必殺技是小無相功
諸位英雄,在下慕容復,我的必殺技是斗轉星移
以上實現,我們定義了一個map來儲存幾位大佬,map的key是大佬的名字,value是該大佬的絕技。greet是我們新定義的型別,
其對應基本型別string,該greet實現了Handler介面,列印出自我介紹的資訊。
關於上面的實現,我們可以發現,有兩點不太好:
首先我們先解決第一個問題,根據我們具體做的事情定義一個更有意義的方法名,比如例子中是自我介紹,
那麼我們使用selfintroduction是不是要比DoFunc這個語意的方法要好呢。
如果呼叫者改了方法名,那麼就不能實現Handler介面,還要使用DoEach方法怎麼辦?那就是由提供DoEach函數的負責提供Handler的實現,
我們改造下程式碼如下:
type HandlerFunc func(k, v interface{}) func (f HandlerFunc) DoFunc(k, v interface{}) { f(k, v) }
上面程式碼我們定義了一個新的型別HandlerFunc,它是一個func(k, v interface{})型別,然後這個新的HandlerFunc實現了Handler介面(原始實現方式中的
type Handler interface { DoFunc(k, v interface{}) }
),DoFunc方法的實現是呼叫HandlerFunc本身,因為HandlerFunc型別的變數就是一個方法。現在我們使用這種方式實現同樣的效果。
完整程式碼如下:
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,我的必殺技是%sn", 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)) }
輸出:
諸位英雄,在下喬峰,我的必殺技是龍爪手
諸位英雄,在下鳩摩智,我的必殺技是小無相功
諸位英雄,在下慕容復,我的必殺技是斗轉星移
還是差不多原來的實現,只是把原介面方法名DoFunc改為selfintroduction。HandlerFunc(g.selfintroduction)不是方法的呼叫,而是轉型,因為selfintroduction和HandlerFunc是同一種型別,
所以可以強制轉型。轉型後,因為HandlerFunc實現了Handler介面,所以我們就可以繼續使用原來的DoEach方法了。
現在解決了命名的問題,但是每次強制轉型是不是不太好?我們繼續重構下,可以採用新定義一個函數的方式,幫助呼叫者強制轉型。
完整程式碼如下:
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,我的必殺技是%sn", 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函數,幫助呼叫者強制轉型,呼叫者就不用自己做了。
現在我們發現EachFunc函數接收的是一個func(k, v interface{})型別的函數,沒有必要實現原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,我的必殺技是%sn", k, v) } func main() { persons := make(map[interface{}]interface{}) persons["喬峰"] = "龍爪手" persons["鳩摩智"] = "小無相功" persons["慕容復"] = "斗轉星移" EachFunc(persons, selfintroduction) }
以上關於函數型介面就寫完了,如果大家仔細留意,發現和我們自己平時使用的http.Handle方法非常類似,其實介面http.Handler就是這麼實現的。
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) }
這是一種非常好的技巧,提供兩種函數,既可以以介面的方式使用,也可以以方法的方式,
對應我們例子中的DoEach和EachFunc這兩個函數,靈活方便,也更符合自然語言規則吧。
無論從事什麼行業,只要做好兩件事就夠了,一個是你的專業、一個是你的人品,專業決定了你的存在,人品決定了你的人脈,剩下的就是堅持,用善良專業和真誠贏取更多的信任。
到此這篇關於Golang介面型函數使用技巧的文章就介紹到這了,更多相關Golang介面使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45