Skip to content

Instantly share code, notes, and snippets.

@MichaelSnowden
Created May 17, 2023 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MichaelSnowden/93658a54b96b3d7033d3415e217382e6 to your computer and use it in GitHub Desktop.
Save MichaelSnowden/93658a54b96b3d7033d3415e217382e6 to your computer and use it in GitHub Desktop.
How to emulate virtual methods in Go
// Package main demonstrates how to emulate virtual methods in Go.
// This implementation doesn't show what the constructors should be; nor does it practice proper information-hiding.
package main
import (
"fmt"
"math"
)
type Shape interface {
Perimeter() float64
}
type Polygon interface {
SideLengths() []float64
}
type BasePolygon struct {
// instance is the field that makes virtual methods possible. It is usually set to this object.
// If someone wants to borrow some functionality from BasePolygon, e.g.
// BasePolygon.Perimeter, they will want to override this to their own implementation, usually a leaf in the tree
// of descendents.
instance Polygon
sideLengths []float64
}
// Perimeter is one of the methods whose implementation we want to borrow in Square
func (s BasePolygon) Perimeter() float64 {
p := 0.0
for _, l := range s.instance.SideLengths() {
p += l
}
return p
}
func (s BasePolygon) SideLengths() []float64 {
return s.sideLengths
}
// RegularPolygon is the object that wants to borrow the implementation of Perimeter.
type RegularPolygon struct {
*BasePolygon
n int
}
// SideLengths is the method we want to override because it gives us the flexibility to change what
// the SideLengths are, without having to recompute the whole array of side lengths until it is required.
func (s RegularPolygon) SideLengths() []float64 {
const (
// perimeter is 2π because the radius is 1.
perimeter = 2 * math.Pi
)
sideLength := math.Tan(perimeter / float64(s.n))
sideLengths := make([]float64, s.n)
for i := 0; i < s.n; i++ {
sideLengths[i] = sideLength
}
return sideLengths
}
func main() {
{
var rp RegularPolygon
rp = RegularPolygon{
BasePolygon: &BasePolygon{
instance: &rp,
},
n: 10,
}
fmt.Printf("The perimeter of the regular %d-gon is %.4fπ\n", rp.n, rp.Perimeter()/math.Pi)
}
{
var ngon BasePolygon
ngon = BasePolygon{
instance: &ngon,
sideLengths: []float64{1.0, 2.0, 3.0, 4.0},
}
fmt.Printf("The perimeter of this static polygon is %.2f\n", ngon.Perimeter())
}
}
The perimeter of the regular 10-gon is 2.3127π
The perimeter of this static polygon is 10.00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment