Go-Writing中的结构类型和多态性一种方法,可以对具有相同字段的两种类型进行操作
在其他语言(如Scala和OCaml)中使用结构化类型之后,我开始研究Go,我正在尝试在这两种语言之间映射一些惯用技术。考虑以下类型Go-Writing中的结构类型和多态性一种方法,可以对具有相同字段的两种类型进行操作,go,structural-typing,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 fie
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
。如果CartesianPoint
和CoordinatePoint
类型为x
和y
字段定义了getter和setter方法,我们可以将XYPoint
定义为这些方法的公共接口,允许我们对这两种类型进行操作,但目前,接口不能声明字段,只能声明方法
基于此,我有几个问题:
ConvertXYToPolar
我发现嵌入式类型、隐式接口满足和基于接口的多态性的简单性是促进代码重用性和可维护性的一种非常简单和吸引人的技术组合,但是接口定义中的禁止字段使得Go的结构化类型功能在我看来有点有限。我是否缺少一个简单的解决方案?通常的方法是使用合成:
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)
您可以使用调用函数,将点
作为参数
doAThingWithAPoint(cp.Point)
为了让您的点可以互换地传递,您必须定义一个接口
type Pointer interface {
GetPoint() *Point
}
func (cp CoordinatePoint) GetPoint() *Point {
return &cp.Point
}
然后,您可以使用指针定义函数:
func doSomethingWith(p Pointer) {
log.Println(p.GetPoint())
}
另一种解决方案是基于定义GetX
、SetX
、GetY
和SetY
的接口,但我个人认为这种方法比必要的方法更重、更详细。我的第一稿是这样的
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
}
通常,惯用的方法是使用getter和setter。不太方便?也许。但至少现在是这样
对。这就是duck类型的本质。将接受与接口匹配的任何类型,而无需显式实现。编辑:根据对这个答案的评论,我误解了这个问题。答案是否定的,您需要为这些结构添加方法以匹配除interface{}
之外的接口
是的,使用getter和setter
也许吧。我能理解为什么接受者和接受者可能被认为不那么方便。但据我所知,他们并没有严格限制你能做什么
对。这是我在其他代码和标准库中看到的方法
除了此方法不支持多态性,因此不满足问题中概述的需求,即将坐标点或笛卡尔点传递给函数,该函数从坐标点或笛卡尔点计算极点。根据此解决方案,您不能将ConvertXYToPolar作为func(Point)PolarPoint
写入,也不能接受CartesianPoint,例如。@burfl答案不完整。我想你的问题现在已经解决了。很好的补充。我真的很喜欢这种方法。我本来会使用一个带有getX()、setX()、getY()和setY()的接口,但我认为这并没有您的解决方案那么优雅+1我很高兴在评论您的答案之前我恢复了精力@dystroy。编辑后会更有帮助。我添加了第六个问题。在不修改现有代码的情况下添加ConvertXYToPolar
方法的想法对我很有吸引力,在业界似乎是一种常见的场景。您是否认为不支持接口定义中的字段是一个令人信服的理由?从语义上讲,接口与实现是隔离的,这是一个很好的理由。在我个人看来,接触太多会降低灵活性。当然,太多的隔离也有缺点,但平衡看起来很好。作为对(2)的回应,您如何在不修改协调点
和笛卡尔点
的情况下做到这一点?我理解duck类型,以及如果结构已经用getter/setter方法定义了,该怎么做,但我的观点是按原样使用这两个结构。很抱歉,我误解了这一点。我认为(2)的意思是“不做任何事情来显式实现接口”。那么答案是“不”;您必须添加某种方法。看看@dystroy的解决方案。它很干净。