原文鏈接:https://www.zhoubotong.site/post/51.html
什么是介面型函式?顧名思義介面函式指的是用函式實作介面,這樣在呼叫的時候就會非常簡便,這種方式適用于只有一個函式的介面,
這里以迭代一個map為例,演示這一實作的技巧,
常規介面實作
defer陳述句用于延遲函式呼叫,每次會把一個函式壓入堆疊中,函式回傳前再把延遲的函式取出并執行,延遲函式可以有引數:
-
延遲函式的引數在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,我的必殺技是%s\n", 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介面,列印出自我介紹的資訊,
介面型函式出場
關于上面的實作,我們可以發現,有兩點不太好:
-
因為必須要實作Handler介面,DoFunc這個方法名不能修改,不能定義一個更有意義的名字
-
必須要新定義一個型別,才可以實作Handler介面,才能使用DoEach函式
首先我們先解決第一個問題,根據我們具體做的事情定義一個更有意義的方法名,比如例子中是自我介紹,
那么我們使用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,我的必殺技是%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))
}
輸出:
諸位英雄,在下喬峰,我的必殺技是龍爪手 諸位英雄,在下鳩摩智,我的必殺技是小無相功 諸位英雄,在下慕容復,我的必殺技是斗轉星移
還是差不多原來的實作,只是把原介面方法名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,我的必殺技是%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函式,幫助呼叫者強制轉型,呼叫者就不用自己做了,
現在我們發現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,我的必殺技是%s\n", 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這兩個函式,靈活方便,也更符合自然語言規則吧,
無論從事什么行業,只要做好兩件事就夠了,一個是你的專業、一個是你的人品,專業決定了你的存在,人品決定了你的人脈,剩下的就是堅持,用善良專業和真誠贏取更多的信任,轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/494129.html
標籤:Go
上一篇:7.CFileDialog的5個讀取檔案資訊的函式 -windows編程
下一篇:多執行緒(三)-執行緒調度
