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

从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 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).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?