I started looking into Go after playing around with structural typing in other languages like Scala and OCaml, and I'm trying map some of the idiomatic techniques between the languages. Consider the following types
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
}
Let's say we'd like to write a method which operates on both of these types to compute their polar coordinate representations, func ConvertXYToPolar(point XYPoint) PolarPoint
. If the CartesianPoint
and CoordinatePoint
types defined getter and setter methods for the x
and y
fields we could define XYPoint
as a common interface with those methods, allowing us to operate on both types, but as it stands, interfaces cannot declare fields, only methods.
Based on this, I have a few questions:
ConvertXYToPolar
without using the empty interface type as the parameter and manually converting?I find the simplicity of embedded types, implicit interface satisfaction, and interface-based polymorphism to be a very simple and appealing combination of techniques to promote code reusability and maintainability, but forbidding fields in interface definitions makes Go's structural typing capabilities somewhat limited from my perspective. Am I missing a simple solution?
The usual way is to use composition :
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 syntax makes this composition mostly feel like inheritance in other languages. You can for example do this :
cp := CoordinatePoint{}
cp.x = 3
log.Println(cp.x)
And you can call functions taking a Point
as parameter with
doAThingWithAPoint(cp.Point)
To let your points be passed interchangeably, you would have to define an interface
type Pointer interface {
GetPoint() *Point
}
func (cp CoordinatePoint) GetPoint() *Point {
return &cp.Point
}
Then you would be able to define functions taking a Pointer
:
func doSomethingWith(p Pointer) {
log.Println(p.GetPoint())
}
Another solution would be based on an interface defining GetX
, SetX
, GetY
and SetY
but I personally find this approach much heavier and more verbose than necessary.
My first draft would look like this,
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With