Golang整洁接口最佳实践

2021-06-20  本文已影响0人  翟志军

翻译自:https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/

woman-3169726_640.jpg

Go中的接口允许我们将不同的类型暂时视为同一数据类型。它们是 Go 程序员工具箱的核心,但新的 Go 开发者往往会使用不当......导致代码难以阅读,易于产生Bug。让我们来看看Golang接口的一些最佳实践。

我经常以标准库为例,来说明如何写出干净的Go接口。标准的错误接口很简单:

type error interface {
    Error() string
}

error接口封装了任何有Error()方法的类型。该方法不接受任何参数,并返回一个字符串。例如,让我们定义一个表示网络问题的结构。

type networkProblem struct {
    message string
    code    int
}

然后,我们定义一个Error()方法:

func (np networkProblem) Error() string {
    return fmt.Sprintf("network error! message: %s, code: %v", np.message, np.code)
}

Now, we can use an instance of the networkProblem struct wherever an error is accepted.

现在,我们可以在任何接受到错误的地方使用networkProblem结构的实例:

func handleErr(err error) {
    fmt.Println(err.Error())
}

np := networkProblem{
    message: "we received a problem",
    code:    404,
}

handleErr(np)

// prints "network error! message: we received a problem, code: 404"

坚持小接口

如果你只能从这篇文章中得到一条建议,那就是:让接口小一点! 接口的目的是为了定义准确表示一个想法或概念所必需的最小行为。

下面是标准HTTP包中一个更大的接口的例子,它是定义最小行为的好例子:

type File interface {
    io.Closer
    io.Reader
    io.Seeker
    Readdir(count int) ([]os.FileInfo, error)
    Stat() (os.FileInfo, error)
}

任何满足接口行为的类型都可以被HTTP包当作一个文件来处理。这很方便,因为HTTP包不需要知道它处理的是磁盘上的文件、网络缓冲区,还是简单的[]字节。

接口应该没有满足类型的知识

一个接口不应该关心具体的类型。

下面,假设我们构建一个用于描述一辆汽车的组件的接口:

type car interface {
    GetColor() string
    GetSpeed() int
    IsFiretruck() bool
}

GetColor()GetSpeed() 方法是汽车领域的知识。而 IsFiretruck() 则是反模式。此接口应该关注所有汽车的通用方法,而不应该关心它是否是一台消防车。 否则,我们还必须在这个接口中增加:IsPickup(), IsSedan(), IsTank() 等等,没完没了了。

相反,当给定一个car的接口实例时,开发应该根据类型断言的原生功能来推断出子类型。或者,如果子接口需要一个子接口,它可以定义为:

type firetruck interface {
    car
    HoseLength() int
}

firetruck接口继承了汽车的必要方法,并增加了一个额外的必要方法,使汽车成为消防车。

接口不是类

接口不是类,应该是小的。

接口不需要构造函数或析构函数,因为它没有必要进行数据的初始化和销毁。

接口在本质上不是分层的,尽管有语法糖来创建接口,而这些接口恰好是其他接口的超集。

接口只负责定义函数签名,不关心其具体实现。在结构方法中,定义一个接口可以减少重复代码。例如,如果5种类型实现了错误接口,它们就需要分别实现Error()函数。

上一篇下一篇

猜你喜欢

热点阅读