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/


评论
  目录