Golang-Interface能怎么用?


Golang Interface 应用

Interface是什么?

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

// 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实现一下行不行呀?

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)
}
// 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),但是对于使用者来说是完全无感知的。

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的打点。

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自己做了一些转换。
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  fishvar 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/


  目录