Golang必備技巧:介面型函數

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

介面型函數,指的是用函數實現介面,這樣在調用的時候就會非常簡便,我稱這種函數,為介面型函數,這種方式使用於只有一個函數的介面。

我們以迭代一個map為例,示範這一技巧,這種方式有點類似於groovy中Map的each方法一樣,也是Gradle裡each閉包。

原始介面實現

1234567891011
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兩個參數,這就是一個介面了,我們後面會實現他,具體做什麼由我們的實現決定。

然後我們定義了一個Each函數,這個函數的功能,就是迭代傳遞過來的map參數,然後把map的每個key和value值傳遞給Handler的Do方法,去做具體的事情,可以是輸出,也可以是計算,具體由這個Handler的實現來決定,這也是面向介面編程。

現在我們就以新學期開學,大家自我介紹為例,示範使用我們剛剛定義的Each方法和Handler介面。這裡我們假設有三個學生,分別為:張三,李四和王五,他們每個人都要介紹自己的名字和年齡。

12345678910111213141516
type welcome stringfunc (w welcome) Do(k, v interface{}) {fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)}func main() {persons := make(map[interface{}]interface{})persons["張三"] = 20persons["李四"] = 23persons["王五"] = 26var w welcome = "大家好"Each(persons, w)}

以上實現,我們定義了一個map來儲存學生們,map的key是學生的名字,value是該學生的年齡。welcome是我們新定義的類型,對應基本類型string,該welcome實現了Handler介面,列印出自我介紹。

介面型函數出場

以上實現,主要有兩點不太好:

  1. 因為必須要實現Handler介面,Do這個方法名不能修改,不能定義一個更有意義的名字
  2. 必須要新定義一個類型,才可以實現Handler介面,才能使用Each函數

首先我們先解決第一個問題,根據我們具體做的事情定義一個更有意義的方法名,比如例子中是自我介紹,那麼使用selfInfo要比Do這個乾巴巴的方法要好的多。

如果調用者改了方法名,那麼就不能實現Handler介面,還要使用Each方法怎麼辦?那就是由提供Each函數的負責提供Handler的實現,我們添加代碼如下:

12345
type HandlerFunc func(k, v interface{})func (f HandlerFunc) Do(k, v interface{}){f(k,v)}

以上代碼,我們定義了一個新的類型HandlerFunc,它是一個func(k, v interface{})類型,然後這個新的HandlerFunc實現了Handler介面,Do方法的實現是調用HandlerFunc本身,因為HandlerFunc類型的變數就是一個方法。

現在我們使用這種方式實現同樣的效果。

1234567891011121314151617
type welcome stringfunc (w welcome) selfInfo(k, v interface{}) {fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)}func main() {persons := make(map[interface{}]interface{})persons["張三"] = 20persons["李四"] = 23persons["王五"] = 26var w welcome = "大家好"Each(persons, HandlerFunc(w.selfInfo))}

還是差不多原來的實現,只是把方法名Do改為selfInfo。HandlerFunc(w.selfInfo)不是方法的調用,而是轉型,因為selfInfo和HandlerFunc是同一種類型,所以可以強制轉型。轉型後,因為HandlerFunc實現了Handler介面,所以我們就可以繼續使用原來的Each方法了。

進一步重構

現在解決了命名的問題,但是每次強制轉型不太好,我們繼續重構,可以採用新定義一個函數的方式,協助調用者強制轉型。

123456789101112131415161718192021
func EachFunc(m map[interface{}]interface{}, f func(k, v interface{})) {Each(m,HandlerFunc(f))}type welcome stringfunc (w welcome) selfInfo(k, v interface{}) {fmt.Printf("%s,我叫%s,今年%d歲\n", w,k, v)}func main() {persons := make(map[interface{}]interface{})persons["張三"] = 20persons["李四"] = 23persons["王五"] = 26var w welcome = "大家好"EachFunc(persons, w.selfInfo)}

新增了一個EachFunc函數,協助調用者強制轉型,調用者就不用自己做了。

現在我們發現EachFunc函數接收的是一個func(k, v interface{})類型的函數,沒有必要實現Handler介面了,所以我們新的類型可以去掉不用了。

12345678910111213
func selfInfo(k, v interface{}) {fmt.Printf("大家好,我叫%s,今年%d歲\n", k, v)}func main() {persons := make(map[interface{}]interface{})persons["張三"] = 20persons["李四"] = 23persons["王五"] = 26EachFunc(persons, selfInfo)}

去掉了自訂類型welcome之後,整個代碼更簡潔,可讀性更好。我們的方法含義都是:

  1. 讓這學生自我介紹
  2. 讓這些學生起立
  3. 讓這些學生早讀
  4. 讓這些學生…

都是這種預設,方法處理,更符合自然語言規則。

延伸

以上關於函數型介面就寫完了,如果我們仔細留意,發現和我們自己平時使用的http.Handle方法非常像,其實介面http.Handler就是這麼實現的。

1234567891011
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)}

這是一種非常好的技巧,提供兩種函數,既可以以介面的方式使用,也可以以方法的方式,對應我們例子中的Each和EachFunc這兩個函數,靈活方便。

最後,附上完整的原始碼:

1234567891011121314151617181920212223242526272829303132333435363738394041
package mainimport ("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["張三"] = 20persons["李四"] = 23persons["王五"] = 26EachFunc(persons, selfInfo)}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.