Golang-Interface能怎么用?


Golang Interface 应用

Interface是什么?

在Golang中,interface是一组method的集合,duck-type programing的一种体现。不关心属性(数据),只关心行为(方法)。可以认为interface是一种协议,一种为了双方交流而做出的约定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
Error() string
}

// ----------------
// 定义自己的err类型
type MyErr struct {}

// 实现error接口
func (e *MyErr)Error() string {
return "my error"
}

Interface能干什么?

  • writing generic algorithm (泛型编程)
  • hiding implementation detail (隐藏具体实现)
  • providing interception points (提供拦截点)

writing generic algorithm

golang的泛型最早1.17才提供版本支持。用interface实现一下行不行呀?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package sort

type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}

// ------------------

type BestLanguage struct {
Name string
Score int
}

func (b BestLanguage) String() string {
return fmt.Sprintf("%s:%d", b.Name, b.Score)
}

type Languages []BestLanguage

// 实现了sort的Interface,不知道是怎么排序的,但拥有了排序的能力
func (l Languages) Len() int {
return len(Languages{})
}

func (l Languages) Less(i, j int) bool {
return l[i].Score > l[j].Score
}
func (l Languages) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}


func main() {
languages := []BestLanguage{
{"Chinese", 5},
{"English", 4},
{"Japanese", 3},
}

fmt.Println(languages)
sort.Sort(Languages(languages))
fmt.Println(languages)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Return concrete types, receive interfaces as parameter.
func ConvertInterfaceToString(v interface{}) string {
var vstr string
switch v.(type) {
case int:
vstr = strconv.Itoa(v.(int))
case int32:
vstr = strconv.Itoa(int(v.(int32)))
case int64:
vstr = strconv.FormatInt(v.(int64), 10)
case float64:
vstr = strconv.Itoa(int(v.(float64)))
case string:
vstr = v.(string)
default:
}
return vstr
}

hiding implement detail

三个函数返回的具体 struct (都实现了 Context interface),但是对于使用者来说是完全无感知的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}

// ------------------

type cancelCtx struct {
Context
mu sync.Mutex
done chan struct{}
children map[canceler]struct{}
err error
}
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) //返回 cancelCtx

type timerCtx struct {
cancelCtx
timer *time.Timer
deadline time.Time
}
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) //返回 timerCtx

type valueCtx struct {
Context
key, val interface{}
}
func WithValue(parent Context, key, val interface{}) Context //返回 valueCtx

providing interception points

实现RoundTripper接口,可以在http请求前后写入自己的逻辑,如apm,jaeger的打点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package http

type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}

func WrapClient(c *http.Client) *http.Client {
if c == nil {
c = http.DefaultClient
}
copied := *c
copied.Transport = wrapRoundTripper(copied.Transport)
return &copied
}

func wrapRoundTripper(r http.RoundTripper) http.RoundTripper {
if r == nil {
r = http.DefaultTransport
}
rt := &roundTripper{r: r}
return rt
}

type roundTripper struct {
r http.RoundTripper
}

func (r *roundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// do something
resp, err := r.r.RoundTrip(req) // real request
// do something
return resp, err
}

值接收 or 指针接收

实现接口的时候应该用值还是指针?

  1. 赋值时必须用指针。
  2. 初始化为指针类型,编译指定没问题。
  3. ❎表示编译不通过,因为对象没有声明接口的全部方法。
  4. 指针类型没有声明全部,但是能编译通过,因为Go自己做了一些转换。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
type Animal interface {
Eat(food string)
Say()
}

// 全部值
type Dog struct {
food string
}

func (d Dog) Eat(food string) {
d.food = food
}
func (d Dog) Say() {
fmt.Println("dog like ", d.food)
}

// 全部指针
type Cat struct {
food string
}

func (d *Cat) Eat(food string) {
d.food = food
}
func (d *Cat) Say() {
fmt.Println("cat like ", d.food)
}

// 有值、有指针
type Fish struct {
food string
}

func (d *Fish) Eat(food string) {
d.food = food
}
func (d Fish) Say() {
fmt.Println("fish like ", d.food)
}

func main() {
var dog Animal = Dog{}
var dog Animal = &Dog{}
var dog Animal = new(Dog)
dog.Eat("bone")
dog.Say()
// dog like # 没吃上,赋值失败

var cat Animal = Cat{}
//var cat Animal = &Cat{}
var cat Animal = new(Cat)
cat.Eat("fish")
cat.Say()
// cat like fish

var fish Animal = Fish{}
var fish Animal = new(Fish)
fish.Eat("small fish")
fish.Say()
// fish like small fish
}

参考

http://legendtkl.com/2017/06/12/understanding-golang-interface/


  目录