Golang 代码中设计:接口的函数实现

这个系列文章我会总结一下我在Golang中经常遇到的一些代码设计

这篇我们看看接口的函数实现,对于像我这样从其他语言转过来的开发者,函数类型以及函数类型方法还是需要理解清楚的。工作中对接口的实现也大多是通过自定义结构体作为类型,然后再为它实现接口,比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
golang复制代码// alertmanager/notify/notify.go:220
// 约定接口
type Stage interface {
Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error)
}
// 自定义类型实现接口
type RoutingStage map[string]Stage
func (rs RoutingStage) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context.Context, []*types.Alert, error) {
receiver, ok := ReceiverName(ctx)
if !ok {
return ctx, nil, errors.New("receiver missing")
}
s, ok := rs[receiver]
if !ok {
return ctx, nil, errors.New("stage for receiver missing")
}
return s.Exec(ctx, l, alerts...)
}

这是一种很常见也很好理解的设计,约定接口Stage然后自定义类型RoutingStage来实现接口,这样RoutingStage就可以被所有使用Stage的位置使用了,但我们看看下面这段:

1
2
3
4
5
golang复制代码// alertmanager/notify/notify.go:234
type StageFunc func(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context context.Context, []*types.Alert, error)
func (f StageFunc) Exec(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context context.Context, []*types.Alert, error) {
return f(ctx, l, alerts...)
}

这里对比类和实例的概念,把StageFunc这样定义函数签名的称为函数类型,这个函数类型对应的函数称为函数值,这段代码中定义了一个函数类型 StageFunc,还为这个函数类型定义了Exec方法,这样的话这个函数类型是满足Stage接口的,在Exec函数中用这个函数值调用Exec方法时接收的参数调用了这个函数值自己。

举个例子,如果一个函数aFunc满足func(ctx context.Context, l log.Logger, alerts ...*types.Alert) (context context.Context, []*types.Alert, error)这样的签名,但这时它还不是StageFunc类型的函数,可以使用bFunc:=StageFunc(aFunc)把这个函数值转为StageFunc类型的函数值,这时候既可以直接调用bFunc(),还可以bFunc.Exec()调用。

这是Golang中一种不是特别常见的设计,当然这种设计的重点也不是对一个函数两种调用方式,而是如何使用函数来实现接口。在net/http的源码中的HandlerFunc也是这种设计方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
golang复制代码// net/http/server.go:2065
// 约定接口
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// 接口的函数实现
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
// 同样的,在函数类型 HandlerFunc 的 ServeHTTP 方法中
// 用方法接收的参数直接调用函数值
f(w, r)
}

// 使用接口
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}

这里的HandlerFunc类似一个转换器,把用户自定义的函数转为特定的HandlerFunc类型,然后通过调用这个类型的方法来调用这个函数,这样相当于开放一个函数签名给用户,用户定义任意满足函数签名的函数就可以被Server使用。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%