将多种类型合并为一种,无需连接

时间:2017-07-10 15:57:41

标签: go types

在Go项目中,我必须定义两种不同的"形状"实现名为MyObject的接口的类型。形状本身是在外部库中定义的类型,并且不实现任何共享接口。

MyObject看起来像

type MyObject interface {
    GetShape() *Shape //some unified return value
}

形状看起来像

type Circle struct {
    Radius int
    X int
    Y int
}

type Square struct {
   X int
   Y int
   W int
   H int
}

func NewCircle(x int, y int, radius int) Circle
func NewSquare(x int, y int, w int, h int) Square

我有一个实施MyObject的球和一个盒子:

type Ball struct {
    shape *Circle
}

type Box struct {
    shape *Square
}

func (b *Ball) GetShape() *Shape {
    return b.shape
}

func (s *Square) GetShape() *Shape {
    return s.shape
}

通过接口这似乎很简单 - 但是我们无法在这种情况下使用它,因为Circle和Square没有实现相同的方法,而且它们不在我们正在工作的包中

对于使用圆形和方形的方法,我需要使用类似

的方法
testCircleSquare(circle *Circle, square *Square) bool {}
testSquareSquare(square1 *Square, square2 *Square) bool {}

如何区分或使这两个对象更通用?到目前为止,我唯一的想法是将它们集成到像

这样的类型中
type Shape struct {
    circle *Circle
    square *Square
}

并检查零圈或方形值以确定使用哪个,但如果我添加更多形状,这似乎很难维护。

2 个答案:

答案 0 :(得分:2)

@Adrian already explained what's wrong with using interface{} here

相反,请使用Adapter Pattern。创建自己的Shape界面并为预制形状制作适配器。

Shape接口(它应该被称为Shape2D,因为3D形状表现不同)可能看起来像这样。这为您提供了类型系统的优势,并具有统一的形状界面。

type Shape interface {
    Area() float32
    Perimeter() float32
    X() int
    Y() int
}

然后围绕现有对象创建适配器。不需要包装器,您可以为该类型定义别名。 (external此处表示Circle和Square来自其他一些包。)

type ShapeCircle external.Circle

func (self ShapeCircle) Area() float32 {
    return math.Pi * float32(self.Radius) * float32(self.Radius)
}

...and so on...

type ShapeSquare external.Square

func (self ShapeSquare) Area() float32 {
    return float32(self.W) * float32(self.H)
}

...and so on...

现在你可以 Circle&Square对象复制到它们的Shape适配器并将它们用作Shape。

c := external.Circle{ Radius: 10, X: 0, Y: 0 }

shape := ShapeCircle(c)

fmt.Println(shape.Area())

你也可以走另一条路。

external.Function( external.Circle(shape) )

再次,这会创建一个副本。

或者,如果您不喜欢复制,可以在ShapeSquare内的ShapeCircle和Square中嵌入Circle。

type ShapeCircle struct {
    external.Circle
}
type ShapeSquare struct {
    external.Square
}

然后你可以像以前一样使用ShapeCircle,但你必须给它一个圆圈。可能想要使用New功能来处理它。

c := ShapeCircle{
    Circle: external.Circle{ Radius: 10, X: 0, Y: 0 }
}

它可以用作Shape。

fmt.Println(c.Area())

并且c.Circle可以用作圆圈。无需复制。

external.Function( c.Circle )

答案 1 :(得分:1)

如果你不能为它们构建一个特定的接口,你唯一真正的选择是空接口interface{},它可以保存任何值。然后,您必须使用type assertionsreflection对值执行任何有用的操作。从设计的角度来看,这是一个不寻常的案例,因为你持有一个你无法做出任何假设的任意值。