接口型函数

接口型函数

在学习极客兔兔大佬的GeeCache时,里面提到了一个接口型函数,之前在使用Go官方的http库时候也遇到过,但是并不知道他原来叫接口型函数,于是赶紧详细学习下这个接口型函数。

概念

所谓接口型函数,就是指用函数实现了接口,并且这个接口只有一个方法的情况。(有点类似Java的函数式接口)

实现过程

接下来用一个例子来迭代,展示接口型函数的使用技巧。

step 1

首先定义一个接口Animal,这个接口只有一个方法Do,接收2个参数k,v,表示动物名称以及动物能做的事情,具体实现由我们决定,然后定义一个forEach遍历,执行Do方法

var animal = map[string]string{
	"dog":   "旺旺叫",
	"cat":   "喵喵叫",
	"tiger": "嗷嗷叫",
}

type Handler interface {
	Do(k, v string)
}

func forEach(m map[string]string, handle Handler) {
	if len(m) > 0 {
		for k, v := range m {
			handle.Do(k, v)
		}
	}
}

type animal string

func (a animal) Do(k, v string) {
	fmt.Printf("%s在%s\n", k, v)
}

func main() {
	var a animal

	forEach(animals, a)
}

上面代码中定义了一个map存储了动物名称和行为的映射,并且我们重定义了一个类型animal,并且它实现了Handler接口,然后在main方法中调用forEach方法执行每个animal的Do方法。

step 2

基于以上代码,功能实际上是已经实现了,但是也有2点不太好的地方:

  1. 因为要实现Handler接口,因此接口的方法名是无法改变的,比如他是Do,就只能用Do
  2. 为了实现Handler,必须定义一个类型,然后才能作为参数传递给forEach方法

因此我们想改掉这2点不太舒服的地方,可以使用接口型函数这个技巧

  • 针对第一个问题,一旦我们改变了方法名称,那就无法实现Handler接口,那forEach方法的参数就无法传递
    我们可以定义一个函数类型来负责实现Handler接口,然后通过类型转换,将我们的方法转为实现了Handler接口的类型,因此我们可以修改代码,不要定义一个string的新类型,直接定义一个函数的新类型,这个函数就是接口需要实现的函数
type HandlerFunc func(k, v string)

func (h HandlerFunc) Do(k, v string) {
	h(k, v)
}

通过定义HandlerFunc类型,并且这个类型是实现了Do方法的,也就是说这个类型是实现了Handler接口,那么就阔以作为参数传递给forEach方法了:

func cry(k, v string) {
	fmt.Printf("%s在%s\n", k, v)
}

type HandlerFunc func(k, v string)

func (h HandlerFunc) Do(k, v string) {
	h(k, v)
}

func main() {
	forEach(animals, HandlerFunc(cry))
}

从上面代码可以看到,这样子我们就可以自定义方法名称了,这里使用了cry,表示叫,将它转换为HandlerFunc类型,就可以作为参数传递给forEach,这样第一个不好的地方就可以顺利解决了。

  • 经过上面改造,其实已经是用了接口型函数了,咱们定义了一个函数类型,并且这个类型实现了接口,但是每次都要类型转换还是挺烦恼的,我们可以进一步优化,定义一个方法,来处理这个类型转换
    比如:
func cry(k, v string) {
	fmt.Printf("%s在%s\n", k, v)
}

type HandlerFunc func(k, v string)

func (h HandlerFunc) Do(k, v string) {
	h(k, v)
}

func forEachFunc(m map[string]string, handle func(k, v string)) {
	forEach(m, HandlerFunc(handle))
}

func main() {
	forEachFunc(animals, cry)
}

这一步实质上就是封装了下forEach方法,帮调用者做了类型转换,对于调用者来说,就可以直接传递对应的方法。
而且对于这样调用forEachFunc(animals, cry),我们发现现在也不用定义新类型去实现Handler接口了。

完整代码
package main

import "fmt"

var animals = map[string]string{
	"dog":   "旺旺叫",
	"cat":   "喵喵叫",
	"tiger": "嗷嗷叫",
}

type Handler interface {
	Do(k, v string)
}

func forEach(m map[string]string, handle Handler) {
	if len(m) > 0 {
		for k, v := range m {
			handle.Do(k, v)
		}
	}
}

func cry(k, v string) {
	fmt.Printf("%s在%s\n", k, v)
}

type HandlerFunc func(k, v string)

func (h HandlerFunc) Do(k, v string) {
	h(k, v)
}

func forEachFunc(m map[string]string, handle func(k, v string)) {
	forEach(m, HandlerFunc(handle))
}

func main() {
	forEachFunc(animals, cry)
}

在这种接口型函数使用中,我们提供了两种方法,一个是forEach,一个是forEachFunc,能够支持使用接口作为参数,也能够支持直接使用方法作为参数。

这部分是不是觉得有点熟悉,是滴,就跟我们使用Go的标准库http的Handler方法很像,实际上它的接口http.Handler就是这么实现的,看看源码:

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值