此类问题通常是由以下原因造成的:
- 过早的接口
- 假接口,或“气泡包装”(参见我的咆哮here https://stackoverflow.com/a/67133656/11424673)
- 并行接口
您遇到的错误与第三个问题最相关,但每个错误都存在于此处的示例设计中。
好的接口应该(以它们的名称和方法签名)传达一组行为。接口的名称应该由它包含的方法隐含,因为名称只是应该捕获该行为的本质。
为此目的似乎需要的唯一接口是:
type Mover interface {
Move() MoveResult
}
对于 MoveResult,您可以这样做(如果您愿意,可以将 float 替换为 int):
type MoveResult struct {
Distance, Height float64
}
考虑到以下假设,这种类型可以作为结构体正常工作:
- 移动结果只是数据点(它们不需要是动态的;您可以设置值并保留它们)
- 当未指定时,“额外”数据(例如高度)可以正常使用零值。
现在,实现鸟类型:
type Bird struct {
// some internal data
}
func (b *Bird) Move() MoveResult {
// some movement calculations
return MoveResult{Distance: howFar, Height: howHigh}
}
现在实现 foo:
func foo(m Mover) float64 {
return m.Move().Distance
}
现在在 main 中使用它:
func main() {
foo(&Bird{})
}
现在我们可以添加其他种类Mover
s 到程序并使用它们foo
没有问题。
针对您的评论,在哪里Animal
, AnimalMoveResult
and foo
都是外部的,你不能修改它们:
我如何用我的理解提出问题:有这个功能foo
,旨在接受一个名为Animal
,其特征在于它如何返回专有类型。你想使用你的类型Bird
as an Animal
,但是您无法将所需的所有行为都放入该专有返回类型中。
那么如何解决这个问题呢?
实现您自己的、更广泛的返回类型
type birdMoveResult struct {
// some internal data
}
func (r birdMoveResult) GetDistance() int {
// get the distance
}
func (r birdMoveResult) GetHeight() int {
// get the height
}
现在,实现 Bird 类型
type Bird struct {
// some internal data
}
func (b *Bird) Move() AnimalMoveResult {
var result birdMoveResult
// calculate the move result
return birdMoveResult
}
请注意Move
返回AnimalMoveResult
类型,所以现在Bird
将满足Animal
界面。我们能够做到这一点是因为birdMoveResult
满足AnimalMoveresult
类型,所以在这里返回它是合法的。
剩下的问题是Bird
's Move
方法缩小了宽接口,我们实际上已经失去了您添加的新功能。为了在仍然满足的情况下取回它Animal
接口,我们无法更改Move
方法签名根本不存在。这里有 2 种可能的解决方法。
- 当使用你的鸟类型时,你可以类型断言 https://tour.golang.org/methods/15结果访问额外的方法
var b Bird
// ...
height := b.Move().(birdMoveResult).GetHeight()
- 添加一个新方法
Bird
返回更宽的类型,保留现有的Move
具有相同的签名
func (b *Bird) MoveFull() birdMoveResult {
var result birdMoveResult
// calculate the move result
return birdMoveResult
}
func (b *Bird) Move() AnimalMoveResult {
return b.MoveFull()
}
height := b.MoveFull().GetHeight()
其中任何一个都可以,这主要是一个偏好问题。