2 回答
TA贡献1891条经验 获得超3个赞
使用方法允许您定义接口。假设你有:
func (m *ProbeManager) Run(w *ProbeWorker) {}
您可以创建一个接口:
type Manager interface {
Run(w *ProbeWorker)
}
现在任何需要的东西都*ProbeManager可以Manager代替。这Run与其实现的细节脱钩。这很有用的原因有很多:
它使代码更容易推理并且更容易安全地更改,因为它隐藏了不必要的细节(信息隐藏)
它使代码更易于测试,因为您可以模拟接口并单独测试一小段代码:
type mockManager struct {
run func(w *ProbeWorker)
}
func (m mockManager) Run(w *ProbeWorker) {
m.run(w)
}
func Test(t *testing.T) {
wasCalled := false
m := mockManager{
run: func(w *ProbeWorker) {
wasCalled = true
},
}
// pass m to something that takes a Manager
}
接口还使您能够实现依赖注入。有很多方法,但一个非常简单的方法是提供一个Default实现:
var DefaultManager Manager = &ProbeManager{}
或者基于字符串的注册表:
var managerLookup = map[string]Manager{}
func RegisterManager(nm string, m Manager) {
managerLookup[nm] = m
}
func GetManager(nm string) Manager {
return managerLookup[nm]
}
这非常强大,因为它允许您修改现有包的行为而无需更改其代码。(例如,假设您有一个文件下载器并且您实现了http支持。其他人可以提供ftp支持,并且解析 URL 所需的代码不需要通过使用此注册表方法进行更改)
接口允许您实现类似的方法来解决您会在其他编程语言中发现的问题。它们为您提供了一种通用的多态性(请参阅sort包),您可以通过实现调用相同接口的接口来实现面向方面的编程或猴子修补(考虑gzip.Reader调用底层File. 的任何东西,io.Reader也可以采用 a gzip.Reader,允许您无需更改其余代码即可替换行为)
我可以继续...
TA贡献1815条经验 获得超12个赞
它们实际上都是等价的。接收者像其他所有参数一样被传递到方法中。由于无论什么(调用方法)都需要手头的两种类型,因此定义它并不重要。就个人而言,基于此,我将使用您的三个选项中的最后一个。这对我来说更有意义,因为在其他情况下,您将方法与这两种类型之一相关联,而实际上它同时需要这两种类型。不过,这只是您希望如何组织代码的问题。在性能或应用程序行为方面,两者之间没有任何好处,它们都是一样的。
编辑:最后一点。这些都不会被导出,所以它是一个“私有”或者更确切地说是一种用作包内部助手的方法。没有接收类型的更多理由。
- 2 回答
- 0 关注
- 143 浏览
添加回答
举报