duanmangxie7131 2017-07-10 15:57
浏览 35
已采纳

无需接口即可将多种类型组合为一种

In a Go project, I've got to define two different kinds of "shapes" to types that implement an interface called MyObject. The shapes themselves are types defined in an external library, and do not implement any shared interface.

MyObject looks like

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

Shapes look like

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

I've got a ball and a box that implement 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
}

This seems straightforward enough with interfacing - but we can't use one in this situation since there are no methods implemented by Circle and Square that are identical, plus they are outside of the package we're working in.

For methods using the circle and the square, I need to use methods like

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

How can I distinguish or make these two objects more generic? The only idea I had so far was to containerize them into a type like

type Shape struct {
    circle *Circle
    square *Square
}

and check for nil circle or square values to determine which to use, but this seems hacky and difficult to maintain if I add more shapes.

  • 写回答

2条回答 默认 最新

  • dongzhi6463 2017-07-10 18:43
    关注

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

    Instead, use the Adapter Pattern. Create your own Shape interface and make adapters for the pre-made shapes.

    The Shape interface (it should probably be called Shape2D because 3D shapes behave differently) might look like this. This gives you the advantages of the type system, and having a unified shape interface.

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

    Then create adapters around the existing objects. No wrapper is necessary, you can define an alias for the type. (external here represents that Circle and Square come from some other package).

    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...
    

    Now you can copy Circle and Square objects to their Shape adapters and use them as Shape.

    c := external.Circle{ Radius: 10, X: 0, Y: 0 }
    
    shape := ShapeCircle(c)
    
    fmt.Println(shape.Area())
    

    You can also go the other way.

    external.Function( external.Circle(shape) )
    

    Again, this creates a copy.


    Alternatively, if you don't like the copying, you can embed Circle inside ShapeCircle and Square inside ShapeSquare.

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

    Then you can use ShapeCircle as before, but you have to give it a Circle. Might want to make New function to take care of that.

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

    It can be used as a Shape.

    fmt.Println(c.Area())
    

    And c.Circle can be used as a Circle. No copying necessary.

    external.Function( c.Circle )
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 高德地图点聚合中Marker的位置无法实时更新
  • ¥15 DIFY API Endpoint 问题。
  • ¥20 sub地址DHCP问题
  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办