您正在尝试在Go中使用继承的面向对象设计模式,这不是正确的做法。此外,在Java或其他面向对象语言中,接口名称以"able"结尾。在Go中,传统上接口名称以"er"结尾。
针对您关于“Rock”的问题,我建议所有可能发生碰撞的物体都实现CollisonShape()方法,并返回一个collison.Shaper (例如Circle),用于测试碰撞。这里的
collison是您的包名称。
type Collider interface {
CollisonShape() Shaper
}
func Collide(c1, c2 Collider) bool {
shape1, shape2 := c1.CollisonShape(), c2.CollisonShape()
...
}
type Rock struct {
shape *collison.Circle
...
}
func (r *Rock) CollisonShape() collison.Shaper {
return r.shape
}
如您所见,我们使用一种方法来访问岩石的碰撞形状。这使我们能够编写
if collison.Collide(rock, spaceCraft) {...}
这是关于如何获取Rock碰撞形状的问题的答案。
如果您想避免在Collide()函数中调用CollisonShape()方法,则需要直接传递collison.Shaper。
Collide方法将在collison包中定义。
func Collide(shape1, shape2 Shaper) bool {...}
你需要编写以下代码:
然后你需要编写
if collison.Collide(rock.shape, spacecraft.shape) {...}
这个设计会稍微更高效一些,但代价是代码可读性较差,这被经验丰富的Go程序员所反感。
如果你想让Circle成为rock中的一个嵌入结构体,你需要按照以下方式定义它。将shape嵌入其中可以节省Circle的分配时间和GC的一些工作。
type Rock struct {
shape collison.Circle
....
}
if collison.Collide(&rock.shape, &spacecraft.shape) {...}
如果你想使用匿名嵌套结构体,那么你需要编写如下代码:
type Rock struct {
Circle
....
}
if collison.Collide(&rock.Circle, &spacecraft.Rectangle) {...}
正如你所看到的,代码越来越难于阅读和使用,形状不再是抽象的了。应该仅在极少数确实有意义的情况下使用匿名嵌入式结构体。
通过使用最初建议的CollisonShape()方法,您可以轻松地将Rock结构更改为此结构,而不会破坏任何代码。
type Rock struct {
shape collison.Circle
...
}
func (r *Rock) CollisonShape() collison.Shaper {
return &r.shape
}
现在形成了形状和嵌入结构。使用一个方法来获取形状,将Rock的内部实现与访问形状解耦。您可以更改Rock的内部实现而不需要更改其他地方的代码。
这就是为什么Go不支持继承的原因之一。它会在基类和派生类之间创建非常强的依赖关系和耦合。经验表明,随着代码的发展,人们经常会后悔这样的耦合。对象组合是Go首选、推荐和得到良好支持的做法。
如果效率是您的目标,则每个Collider都应该有一个位置进行更改和一个边界框,其宽度和高度不会更改。您可以使用这些值来进行边界框重叠测试,从而节省一些操作。但这是另外一个故事。
default:
部分的存在是为了说明即使Rock
作为匿名字段拥有Circle
,它也不会被检测为Circle
的Shaper
。 - VonC