接口型函數(shù),指的是用函數(shù)實現(xiàn)接口,這樣在調(diào)用的時候就會非常簡便,我稱這種函數(shù),為接口型函數(shù),這種方式使用于只有一個函數(shù)的接口。
我們以迭代一個map為例,演示這一技巧,這種方式有點類似于groovy中Map的each方法一樣,也是Gradle里each閉包。
原始接口實現(xiàn)
type Handler interface {
Do(k, v interface{})
}
func Each(m map[interface{}]interface{}, h Handler) {
if m != nil && len(m) > 0 {
for k, v := range m {
h.Do(k, v)
}
}
}
首先定義一個Handler接口,只有一個Do方法,接收k,v兩個參數(shù),這就是一個接口了,我們后面會實現(xiàn)他,具體做什么由我們的實現(xiàn)決定。
然后我們定義了一個Each函數(shù),這個函數(shù)的功能,就是迭代傳遞過來的map參數(shù),然后把map的每個key和value值傳遞給Handler的Do方法,去做具體的事情,可以是輸出,也可以是計算,具體由這個Handler的實現(xiàn)來決定,這也是面向接口編程。
現(xiàn)在我們就以新學期開學,大家自我介紹為例,演示使用我們剛剛定義的Each方法和Handler接口。這里我們假設有三個學生,分別為:張三,李四和王五,他們每個人都要介紹自己的名字和年齡。
type welcome string
func (w welcome) Do(k, v interface{}) {
fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)
}
func main() {
persons := make(map[interface{}]interface{})
persons["張三"] = 20
persons["李四"] = 23
persons["王五"] = 26
var w welcome = "大家好"
Each(persons, w)
}
以上實現(xiàn),我們定義了一個map來存儲學生們,map的key是學生的名字,value是該學生的年齡。welcome
是我們新定義的類型,對應基本類型string,該welcome
實現(xiàn)了Handler接口,打印出自我介紹。
接口型函數(shù)出場
以上實現(xiàn),主要有兩點不太好:
- 因為必須要實現(xiàn)Handler接口,Do這個方法名不能修改,不能定義一個更有意義的名字
- 必須要新定義一個類型,才可以實現(xiàn)Handler接口,才能使用Each函數(shù)
首先我們先解決第一個問題,根據(jù)我們具體做的事情定義一個更有意義的方法名,比如例子中是自我介紹,那么使用selfInfo要比Do這個干巴巴的方法要好的多。
如果調(diào)用者改了方法名,那么就不能實現(xiàn)Handler接口,還要使用Each方法怎么辦?那就是由提供Each函數(shù)的負責提供Handler的實現(xiàn),我們添加代碼如下:
type HandlerFunc func(k, v interface{})
func (f HandlerFunc) Do(k, v interface{}){
f(k,v)
}
以上代碼,我們定義了一個新的類型HandlerFunc,它是一個func(k, v interface{})類型,然后這個新的HandlerFunc實現(xiàn)了Handler接口,Do方法的實現(xiàn)是調(diào)用HandlerFunc本身,因為HandlerFunc類型的變量就是一個方法。
現(xiàn)在我們使用這種方式實現(xiàn)同樣的效果。
type welcome string
func (w welcome) selfInfo(k, v interface{}) {
fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)
}
func main() {
persons := make(map[interface{}]interface{})
persons["張三"] = 20
persons["李四"] = 23
persons["王五"] = 26
var w welcome = "大家好"
Each(persons, HandlerFunc(w.selfInfo))
}
還是差不多原來的實現(xiàn),只是把方法名Do改為selfInfo。HandlerFunc(w.selfInfo)
不是方法的調(diào)用,而是轉(zhuǎn)型,因為selfInfo和HandlerFunc是同一種類型,所以可以強制轉(zhuǎn)型。轉(zhuǎn)型后,因為HandlerFunc實現(xiàn)了Handler接口,所以我們就可以繼續(xù)使用原來的Each方法了。
進一步重構(gòu)
現(xiàn)在解決了命名的問題,但是每次強制轉(zhuǎn)型不太好,我們繼續(xù)重構(gòu),可以采用新定義一個函數(shù)的方式,幫助調(diào)用者強制轉(zhuǎn)型。
func EachFunc(m map[interface{}]interface{}, f func(k, v interface{})) {
Each(m,HandlerFunc(f))
}
type welcome string
func (w welcome) selfInfo(k, v interface{}) {
fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)
}
func main() {
persons := make(map[interface{}]interface{})
persons["張三"] = 20
persons["李四"] = 23
persons["王五"] = 26
var w welcome = "大家好"
EachFunc(persons, w.selfInfo)
}
新增了一個EachFunc函數(shù),幫助調(diào)用者強制轉(zhuǎn)型,調(diào)用者就不用自己做了。
現(xiàn)在我們發(fā)現(xiàn)EachFunc函數(shù)接收的是一個func(k, v interface{})類型的函數(shù),沒有必要實現(xiàn)Handler接口了,所以我們新的類型可以去掉不用了。
func selfInfo(k, v interface{}) {
fmt.Printf("大家好,我叫%s,今年%d歲\n", k, v)
}
func main() {
persons := make(map[interface{}]interface{})
persons["張三"] = 20
persons["李四"] = 23
persons["王五"] = 26
EachFunc(persons, selfInfo)
}
去掉了自定義類型welcome
之后,整個代碼更簡潔,可讀性更好。我們的方法含義都是:
- 讓這學生自我介紹
- 讓這些學生起立
- 讓這些學生早讀
- 讓這些學生...
都是這種默認,方法處理,更符合自然語言規(guī)則。
延伸
以上關(guān)于函數(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ù),既可以以接口的方式使用,也可以以方法的方式,對應我們例子中的Each和EachFunc這兩個函數(shù),靈活方便。
最后,附上完整的源代碼:
package main
import (
"fmt"
)
type Handler interface {
Do(k, v interface{})
}
type HandlerFunc func(k, v interface{})
func (f HandlerFunc) Do(k, v interface{}) {
f(k, v)
}
func Each(m map[interface{}]interface{}, h Handler) {
if m != nil && len(m) > 0 {
for k, v := range m {
h.Do(k, v)
}
}
}
func EachFunc(m map[interface{}]interface{}, f func(k, v interface{})) {
Each(m, HandlerFunc(f))
}
func selfInfo(k, v interface{}) {
fmt.Printf("大家好,我叫%s,今年%d歲\n", k, v)
}
func main() {
persons := make(map[interface{}]interface{})
persons["張三"] = 20
persons["李四"] = 23
persons["王五"] = 26
EachFunc(persons, selfInfo)
}
更多閱讀推薦
從Java到Golang快速入門
GitHub上優(yōu)秀的Go開源項目
一個簡單的Golang實現(xiàn)的Socket5 Proxy
一個簡單的Golang實現(xiàn)的HTTP Proxy