duanrang2627
duanrang2627
2018-06-25 20:07
浏览 107
已采纳

从golang包中导出接口而不是结构

Below you can find three different ways of calling a method Name() of a customer struct. The result is exactly the same but each one of the three package exports different things:

package main

import (
    "customer1"
    "customer2"
    "customer3"
    "fmt"
    "reflect"
)

func main() {

    c1 := customer1.NewCustomer("John")
    fmt.Println(c1.Name())

    c2 := customer2.NewCustomer("John")
    fmt.Println(c2.Name())

    c3 := customer3.NewCustomer("John")
    fmt.Println(c3.Name())
}

Output

John
John
John

customer1.go (Export Customer struct and Name() method)

package customer1

type Customer struct {
    name string
}

func NewCustomer(name string) * Customer{
    return &Customer{name: name}
}

func (c *Customer) Name() string {
    return c.name
}

customer2.go (Do not export customer struct. Export only Name() method)

package customer2

type customer struct {
    name string
}

func NewCustomer(name string) *customer {
    return &customer{name: name}
}

func (c *customer) Name() string {
    return c.name
}

customer3.go (Do not export customer struct. Export Customer Interface)

package customer3

type Customer interface {
    Name() string
}
type customer struct {
    name string
}

func NewCustomer(name string) Customer {
    return &customer{name: name}
}

func (c *customer) Name() string {
    return c.name
}

My question is which approach you would recommend and why? Which is better in terms of extensibility and maintainability? Which one you would use for a large project?

It seems that customer3 approach is officially discouraged (// DO NOT DO IT!!!) as you can read here https://github.com/golang/go/wiki/CodeReviewComments#interfaces

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • donglei3370
    donglei3370 2018-06-25 20:49
    已采纳

    Interfaces in Go work (and are used) a bit differently that what you'd expect if you come from other languages such as Java.

    In Go, the object implementing the interface does not need to explicitly say it implements it.

    That has subtle ramifications, such as the consumer of a type being able to decouple from the implementation even if the implementing side did not bother (or think about) creating an interface in the first place.

    So, the idiomatic way in Go would be to use a variation of your first approach.

    You'd define customer1.go exactly as you did (so implementation of the type is as simple as possible).

    Then if necessary you can de-couple in the consumer (your main package in this case) by defining an interface there:

    type Customer interface {
        Name() string
    }
    
    func main() {
    
        var c1 Customer
        c1 := customer1.NewCustomer("John")
        fmt.Println(c1.Name())
    
    }
    

    That way, your main implementation can work with any type that has a Name() method, even if the package implementing the type in the first place did not think about that need.

    To achieve extensibility, this is usually also applied for functions you export that receive parameters.

    If you are exporting a function like this:

    func PrintName(customer Customer) {
        fmt.Println(customer.Name())
    }
    

    Then that function can be invoked with any object that implements Customer (any of your implementations would work for example).

    点赞 评论

相关推荐