Created
May 17, 2023 20:09
-
-
Save MichaelSnowden/93658a54b96b3d7033d3415e217382e6 to your computer and use it in GitHub Desktop.
How to emulate virtual methods in Go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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()) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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