Golang必备技巧:接口型函数

接口型函数,指的是用函数实现接口,这样在调用的时候就会非常简便,我称这种函数,为接口型函数,这种方式使用于只有一个函数的接口。

我们以迭代一个map为例,演示这一技巧,这种方式有点类似于groovy中Map的each方法一样,也是Gradle里each闭包。

原始接口实现

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接口。这里我们假设有三个学生,分别为:张三,李四和王五,他们每个人都要介绍自己的名字和年龄。

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

以上实现,我们定义了一个map来存储学生们,map的key是学生的名字,value是该学生的年龄。welcome是我们新定义的类型,对应基本类型string,该welcome实现了Handler接口,打印出自我介绍。

接口型函数出场

以上实现,主要有两点不太好:

  1. 因为必须要实现Handler接口,Do这个方法名不能修改,不能定义一个更有意义的名字
  2. 必须要新定义一个类型,才可以实现Handler接口,才能使用Each函数

首先我们先解决第一个问题,根据我们具体做的事情定义一个更有意义的方法名,比如例子中是自我介绍,那么使用selfInfo要比Do这个干巴巴的方法要好的多。

如果调用者改了方法名,那么就不能实现Handler接口,还要使用Each方法怎么办?那就是由提供Each函数的负责提供Handler的实现,我们添加代码如下:

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类型的变量就是一个方法。

现在我们使用这种方式实现同样的效果。

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

}

还是差不多原来的实现,只是把方法名Do改为selfInfo。HandlerFunc(w.selfInfo)不是方法的调用,而是转型,因为selfInfo和HandlerFunc是同一种类型,所以可以强制转型。转型后,因为HandlerFunc实现了Handler接口,所以我们就可以继续使用原来的Each方法了。

进一步重构

现在解决了命名的问题,但是每次强制转型不太好,我们继续重构,可以采用新定义一个函数的方式,帮助调用者强制转型。

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函数,帮助调用者强制转型,调用者就不用自己做了。

现在我们发现EachFunc函数接收的是一个func(k, v interface{})类型的函数,没有必要实现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之后,整个代码更简洁,可读性更好。我们的方法含义都是:

  1. 让这学生自我介绍
  2. 让这些学生起立
  3. 让这些学生早读
  4. 让这些学生...

都是这种默认,方法处理,更符合自然语言规则。

延伸

以上关于函数型接口就写完了,如果我们仔细留意,发现和我们自己平时使用的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)
}

这是一种非常好的技巧,提供两种函数,既可以以接口的方式使用,也可以以方法的方式,对应我们例子中的Each和EachFunc这两个函数,灵活方便。

最后,附上完整的源代码:

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上优秀的Go开源项目
一个简单的Golang实现的Socket5 Proxy
一个简单的Golang实现的HTTP Proxy

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • //Clojure入门教程: Clojure – Functional Programming for the J...
    葡萄喃喃呓语阅读 3,616评论 0 7
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 今天我们面临这样的问题,我们手上掌握了很多的资讯,我们打开电视可以看到上百个电视台,我们透过手机和网络可以看到世界...
    优质写作侠阅读 472评论 1 2
  • “巨龙巨龙,你为什么又把王子给吓跑了。”公主生气的跺脚,长裙晃在脚边,露出漂亮的脚踝。巨龙转过脑袋看了她一眼,黑黝...
    楚桑桑桑阅读 598评论 0 0