5

在使用其他语言(如 Scala 和 OCaml)的结构类型之后,我开始研究 Go,并且我正在尝试在语言之间映射一些惯用技术。考虑以下类型

type CoordinatePoint struct {
    x int
    y int
    // Other methods and fields that aren't relevant
}

type CartesianPoint struct {
    x int
    y int
    // Other methods and fields that aren't relevant
}

假设我们想编写一个对这两种类型都进行操作的方法来计算它们的极坐标表示,func ConvertXYToPolar(point XYPoint) PolarPoint. 如果CartesianPointand类型为and字段CoordinatePoint定义了 getter 和 setter 方法,我们可以定义为这些方法的公共接口,允许我们对这两种类型进行操作,但就目前而言,接口不能声明字段,只能声明方法。xyXYPoint

基于此,我有几个问题:

  1. 在 Go 中处理这个的惯用方式是什么?
  2. 可以在不修改现有类型的情况下完成吗?
  3. 我们能否保留类型安全,即避免ConvertXYToPolar在不使用空接口类型作为参数和手动转换的情况下进行定义?
  4. 如果接口和隐式接口满足是 Go 中多态性的主要工具,那么接口定义中字段的禁止性是否受到限制?
  5. getter/setter 方法是否通常在结构上定义以规避此限制?
  6. 设计决定背后是否有令人信服的理由支持接口定义中的字段?

我发现嵌入式类型的简单性、隐式接口满足和基于接口的多态性是一种非常简单且吸引人的技术组合,可以提高代码的可重用性和可维护性,但是接口定义中的禁止字段使 Go 的结构化类型功能在我看来有些受限. 我错过了一个简单的解决方案吗?

4

3 回答 3

11

通常的方法是使用组合:

type Point struct {
    x int
    y int
}

type CoordinatePoint struct {
    Point
    other stuff
}

type CartesianPoint struct {
    Point
    Other methods and fields that aren't relevant
}

Go 语法让这种组合感觉更像是其他语言的继承。例如,您可以这样做:

cp := CoordinatePoint{} 
cp.x = 3
log.Println(cp.x)

您可以调用带有Pointas 参数的函数

doAThingWithAPoint(cp.Point)

为了让你的点可以互换传递,你必须定义一个接口

type Pointer interface {
    GetPoint() *Point
}
func (cp CoordinatePoint) GetPoint() *Point {
    return &cp.Point
}

然后,您将能够定义采用 a 的函数Pointer

func doSomethingWith(p Pointer) {
    log.Println(p.GetPoint())
}

另一种解决方案将基于定义GetX,的接口SetX,但我个人发现这种方法比必要的更重和更冗长。GetYSetY

于 2013-03-29T13:06:50.607 回答
2

我的初稿应该是这样的

package points

type XYPoint struct {
    X, Y int64
}

type CoordinatePoint struct {
    XYPoint
}

type CartesianPoint struct {
    XYPoint
}

type PolarPoint struct {
    R, T float64
}

type XYToPolarConverter interface {
    ConvertXYToPolar(point XYPoint) PolarPoint
}

func (cp *CoordinatePoint) ConvertXYToPolar(point XYPoint) PolarPoint {
    pp := PolarPoint{}
    // ...
    return pp
}

func (cp *CartesianPoint) ConvertXYToPolar(point XYPoint) PolarPoint {
    pp := PolarPoint{}
    // ...
    return pp
}
于 2013-03-29T13:17:55.990 回答
1
  1. 通常,惯用的方法是使用 getter 和 setter。不太方便?也许。但至少目前是这样。
  2. 是的。这就是鸭式打字的精髓。任何与接口匹配的类型都将被接受,无需显式实现。编辑:根据对此答案的评论,我误解了这个问题。答案是否定的,您需要为这些结构添加方法以匹配除interface{}.
  3. 是的,使用 getter 和 setter。
  4. 也许。我明白为什么 getter 和 setter 可能被认为不太方便。但据我所知,它们并没有严格限制你可以做什么。
  5. 是的。这是我在其他人的代码和标准库中看到的方式。
于 2013-03-29T13:18:48.787 回答