将多种类型组合成一个不需要接口 [英] Combine multiple types into one without interfacing

查看:107
本文介绍了将多种类型组合成一个不需要接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



MyObject看起来像

  type MyObject interface {
GetShape()* Shape //一些统一的返回值
}

形状看起来像

 类型Circle结构{
半径int
X int
Y int
}

类型Square结构{
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)

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

 类型Ball结构{
形状*圆形
}

类型结构体{
形状* Square b

$ 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 {}

如何区分或使这两个对象更通用?到目前为止,唯一的想法是将它们装入一个像类型的类型中。
$ b

 类型Shape结构{
circle * Circle
square * Square
}

并检查零圆或方形值确定要使用哪一个,但是如果我添加更多形状,这看起来很诡异且难以维护。

解决方案

@Adrian已经解释了在这里使用 interface {} 有什么问题。
$ b

请使用适配器模式。创建自己的Shape界面,并为预制图形制作适配器。



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

 类型Shape接口{
区域()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)
}

...等等...

类型ShapeSquare external.Square

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

...等等...

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

  c:= external.Circle {Radius:10,X:0,Y:0} 
$ b $ shape:= ShapeCircle c)

fmt.Println(shape.Area())


$ b

  external.Function(external.Circle(shape))

同样,这会创建一个副本。




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

  type ShapeCircle struct { 
external.Circle
}
类型ShapeSquare结构{
external.Square
}

然后你可以像以前一样使用ShapeCircle,但是你必须给它一个Circle。

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

用作Shape。

  fmt.Println(c.Area())



c.Circle 可以用作Circle。不需要复制。

  external.Function(c.Circle)


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.

解决方案

@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 )

这篇关于将多种类型组合成一个不需要接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆