Go中空接口的最佳实践是什么?

3
我正在学习空接口。我发现虽然有很多解释(也在Stackoverflow上),关于空接口的含义和工作原理,但是在何时/为什么使用它们、何时避免使用它们、考虑因素以及选择使用它们的利弊方面的最佳实践信息很少。
在Go聊天室中,我读到一些讨论,说最好尽可能避免使用空接口,但没有适当的论据。其他人则自豪地回应说他们的代码设计中没有任何空接口。
我最感兴趣的是库和框架的用途(旨在供他人重用/扩展)。
现在我正在阅读一个带有相关库的框架代码库,其中许多位置都充满了空接口。其中一些使我感到困惑,我想知道所有用法是否都是必要的。例如,该框架提供“用户管理”和像AppConfigurationUserPreferences这样的东西是空接口。这是因为在代码的后面(接近数据库层)中,用户的电子邮件技术上被视为用户偏好设置。在接口定义中更具体是否更好呢?

2个回答

4
在 Go 的聊天室中,我读到了有关尽可能避免使用空接口的讨论,但缺乏适当的论据。其他人则自豪地表示他们在代码设计中没有使用任何空接口。
最大的问题是你会失去所有类型检查;例如,假设我有一个想要对各种类型的数字进行操作的函数,那么我会写:
func AddOne(n interface{}) int64 {
    switch nn := n.(type) {
        case int:
            return nn + 1
        case int8:
            return nn + 1
        // ... etc...
    }
}

如果我向这个函数传递0.42(float64),或者字符串"asd"会发生什么?如果函数接受int64(func AddOne(n int64)int64),那么我们会在编译时收到关于此的警告,但是使用interface{}您将不会得到任何提示,因为一切都是有效的。
最好的做法是在运行时处理这个问题并且可能会panic()或返回一个错误;显然这要比编译时的错误更加模糊。
这是目前最大的缺点。
我需要查看特定的AppConfiguration和UserPreferences函数的详细信息,但是使用空接口有一些好处,例如当您想要接受某个自定义结构并使用反射根据某些外部数据设置结构上的值时。这本质上就是像encoding/json这样的包所做的事情,但它也通常用于解析某些配置文件等。
听起来AppConfiguration和UserPreferences可能符合这种情况,因为库/框架不知道您的应用程序配置需要哪些设置,或者有哪些用户偏好。但是像我说的那样,没有详细信息很难确定。
另一个用例是当您真的想要接受各种类型时;将参数传递给SQL查询是一个常见的示例,或者fmt.Printf()。
总体而言,最好避免使用interface{},但是如果您发现自己为此而努力,那么最好就使用interface{}并接受缺乏类型的限制。

1
我认为这个问题是关于命名为空接口,例如 type UserPreferences interface{} - Alexander Trakhimenok
感谢@martin-tournoij!使用库+框架时,我只是指代了旨在由他人重用和扩展的代码。有一些被命名为空接口,并且还有像func(a *App)DefaultUserPreferences()interface {}这样的东西,它返回nil。然而,用户首选项应该包括电子邮件,这在数据库中是必需的。 - Arnold Schrijver
1
命名/类型化空接口的一个优点是可以将方法附加到它们上面。通常情况下,编写一些通用的用户认证库需要一些努力,如果没有使用interface{}的话(虽然这也不是不可能,我已经做过了)。但正如我所说的,很难确定;可能存在使接口难以使用的功能或考虑因素。 - Martin Tournoij
1
据我所知,我们无法将方法“附加”到命名接口,仅能附加到“结构类型”。如果我错了,请纠正我。 - Alexander Trakhimenok
你可以将方法附加到任何类型 @AlexanderTrakhimenok (type x string,我已经做过很多次了),但实际上我现在不确定接口类型是否可以。看起来你不能:invalid receiver type named (named is an interface type),所以我想我之前的想法是错误的。 - Martin Tournoij

1
在Go语言中,空的命名接口是没有意义的,因为与其他语言(例如C#)不同,如果任何类型(在C#中为类)与接口签名匹配,则可以将其转换为特定的接口。因此,在Go中,“类”(结构类型)不需要从接口继承(例如,在类型定义时声明接口)。
所以回答你的问题:
在Go中,最佳实践是不定义命名空接口。
更新:感谢下面的评论,我改变了我的想法,并且同意对于文档和IDE中更快速的导航,使用空的命名接口可能有限制的用例。

1
“在Go语言中,空命名接口看起来毫无意义”——除非它确实有意义。 - Jonathan Hall
此外,Go语言中没有类和继承的概念。如果您是在C#或Go语言环境下使用这些术语,就不太清楚了。 - Jonathan Hall
1
出于同样的原因,您可能会使用任何其他基本类型的命名类型:文档。 - Jonathan Hall
1
如果你的标准是stdlib,这里有一个著名的例子。还有许多其他的例子。例如,12,... - Jonathan Hall
3
谢谢提供链接,非常感谢。虽然链接1和2引导到测试代码,但显然可以在其中使用空命名接口进行测试,以测试取决于它的行为。关于“著名示例”,它是一个很好且有效的例子,用于论证,经过研究代码后,我同意出于文档目的使用它是有道理的。而且,在某些用例中,我认为它对IDE可能有用。再次感谢您的教育。 - Alexander Trakhimenok
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接