Golang接口转换为嵌入式结构

时间:2015-03-13 18:03:55

标签: interface go

我想使用Collidable接口

实现碰撞库
type Collidable interface{
    BoundingBox() (float64,float64,float64,float64)
    FastCollisionCheck(c2 Collidable) bool
    DoesCollide(c2 Collidable) bool
    Collide(c2 Collidable)
}

它有预定义的形状,如。

type Circle struct{
X,Y,Radius float64
}

我的想法是我可以做到

type Rock struct{
    collision.Circle
    ....
}

然后实现了Collidable接口,所以我可以将它传递给Spatial Hash Map(期望可以碰撞)。唯一需要做的就是根据我的需要覆盖Collide()函数。

然而,类型圈中的函数无法处理类型摇滚,即使它有一个圆形嵌入也很难。

func (c1 *Circle) DoesCollide(i Collidable) bool{
    switch c2 := value.(type) {
    case Circle:
    //doesn't fire, as it is of type Rock (unknown in this package)
    //Needed is something like
    //if i_embeds_Circle then c2 := i_to_Circle 
    }
}

这可能吗? 还有更好的方法吗?

4 个答案:

答案 0 :(得分:2)

您正在尝试使用具有继承的面向对象设计模式。这不是Go中的方法。此外,接口名称以Java中的“able”或等效的面向对象语言结尾。在Go中,约定是使用'er'结束接口名称。

要回答关于Rock的问题,我建议所有可能碰到另一件事的东西都会实现CollisonShape()方法返回一个collison.Shaper(例如Circle),你将用它来测试collison。这里 collison 是您的包裹的名称。

// This interface is defined in the collison package.
// Any object that may collide must implement that method.
type Collider interface {
    CollisonShape() Shaper
}

// This function defined in the collison package 
// test if two Collider collide into each other.
func Collide(c1, c2 Collider) bool {
    shape1, shape2 := c1.CollisonShape(), c2.CollisonShape()
    ...
}

// This is how you would define an object that can collide.
type Rock struct {
    shape *collison.Circle
    ...
}
// Implements the Collider interface.
// The return type must be the same as in the interface.
func (r *Rock) CollisonShape() collison.Shaper {
    return r.shape
}

如您所见,我们使用一种方法来获取岩石的碰撞形状。这允许我们写

if collison.Collide(rock, spaceCraft) {...}

这回答了关于如何获得“岩石形状”的问题。

如果你想避免在Collide()函数中调用CollisonShape()方法,你必须直接传递collison.Shaper。

方法Collide将在collison包中定义为

func Collide(shape1, shape2 Shaper) bool {...}

然后你必须写

if collison.Collide(rock.shape, spacecraft.shape) {...}

这种设计会稍微提高效率,但要付出代价的是可读性较低的代码,这是经验丰富的Go程序员所不满意的。

如果你想让Circle成为摇滚中的嵌入式结构,那么你必须按照以下方式定义它。嵌入形状可以节省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的首选和推荐,并得到Go的良好支持。

如果效率是你的目标,那么每个碰撞器应该有一个更改的位置和一个宽度和高度不会改变的边界框。您可以使用这些值为边界框重叠测试保存一些操作。但这是另一个故事。

答案 1 :(得分:0)

不确定这是多么偏离,但发布它以防万一它会对你有所帮助。

http://play.golang.org/p/JYuIRqwHCm

答案 2 :(得分:0)

如果您正在调用不可扩展的遗留库(并且无法检测Rock,只能Circle),则似乎只有一个解决方案,传递Circle

请参阅this example,我在其中使用非匿名字段' c'对于Rock
(这意味着Rock必须实现接口,这是对Circle.Shape()的简单委派

type Shaper interface {
    Shape()
}

type Circle struct{}

func (c *Circle) Shape() {}

type Rock struct{ c *Circle }

func (r *Rock) Shape() { r.Shape() }

func DoesShape(s Shaper) {
    fmt.Println("type:", reflect.TypeOf(s))
    switch st := s.(type) {
    case *Circle:
        fmt.Println("Shaper Circle %+v", st)
    default:
        fmt.Println("Shaper unknown")
    }
}

func main() {
    c := &Circle{}
    DoesShape(c)
    r := &Rock{c}
    DoesShape(r.c)
}

然后输出是:

type: *main.Circle
Shaper Circle %+v &{}
type: *main.Circle
Shaper Circle %+v &{}

答案 3 :(得分:0)

我通过稍微修改基类解决了类似的问题。不确定这是否是您想要的解决方案。

import "fmt"

type IShaper interface {
    Shape()
}

type Rect struct{}

func (r *Rect) Shape() {}

type Circle struct{}

func (c *Circle) GetCircle() *Circle { return c }

func (c *Circle) Shape() {}

type Rock struct{ *Circle }

type ShapeWithCircle interface {
    GetCircle() *Circle
}

func DoesShape(s IShaper) {
    if sc, ok := s.(ShapeWithCircle); ok {
        fmt.Printf("Shaper Circle %+v\n", sc.GetCircle())
    } else {
        fmt.Println("Shaper unknown")
    }
}

func main() {
    DoesShape(&Circle{})
    DoesShape(&Rock{&Circle{}})
    DoesShape(&Rect{})
}

也就是说,添加一个简单的函数GetCircle(),以便任何嵌入Circle的函数都具有获取(可能是嵌入的)Circle的功能。然后,任何需要圆的人都可以编写一个接口(此处为ShapeWithCircle),以测试是否定义了GetCircle(),如果是,则调用该接口以获取嵌入式圆。

https://play.golang.org/p/IDkjTPrG3Z5上玩。