Go中的结构类型和多态性 - 编写一种可以在具有相同字段的两种类型上操作的方法

Syl*_*sis 5 go structural-typing

在开始使用其他语言(如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
}
Run Code Online (Sandbox Code Playgroud)

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

基于此,我有几个问题:

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

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

Den*_*ret 9

通常的方法是使用组合:

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
}
Run Code Online (Sandbox Code Playgroud)

Go语法使得这个组合大多感觉像是在其他语言中的继承.你可以这样做:

cp := CoordinatePoint{} 
cp.x = 3
log.Println(cp.x)
Run Code Online (Sandbox Code Playgroud)

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

doAThingWithAPoint(cp.Point)
Run Code Online (Sandbox Code Playgroud)

要让您的点可以互换地传递,您必须定义一个接口

type Pointer interface {
    GetPoint() *Point
}
func (cp CoordinatePoint) GetPoint() *Point {
    return &cp.Point
}
Run Code Online (Sandbox Code Playgroud)

然后你就可以定义函数了Pointer:

func doSomethingWith(p Pointer) {
    log.Println(p.GetPoint())
}
Run Code Online (Sandbox Code Playgroud)

另一种解决方案将基于定义一个接口上GetX,SetX,GetYSetY,但我个人觉得这个方法比需要的更重,更详细.