Monday 6 April 2015

Polymorphism with Go

A few days ago I started making my first steps with the Go language. As I am a real newbie in Go, take everything I am going to write here with a (big) grain of salt.

To a large extend, this (and any next posts on Go) reflect my personal journey with the language, from the very basics, to the (hopefully) mastering a new language.

Although Go takes a (few) step(s) away from the traditional object-oriented languages (think Java, Ruby, C#), so far I find it totally possible to write good object-oriented code.

Take for instance polymorphism. The program below show how you can define and execute code that acts much like overridden polymorphic methods. This code also show one very, very nice Go feature -- strong support for composition. Take a close look at how Rect composes a Square within itself, which is basically how we say that a Rect is a Square (well, you can argue that mathematically it's more like the opposite, but it was easier for me to structure the code this way) and as such, all Square's members are automatically accessed via Rect with no call forwarding. This lets us using simply
this.a * this.b
and not
this.Square.a * this.b
although this would also work fine. In other words, syntactically and semantically, it's very similar to what we achieve with inheritance in traditional OO languages.
package main

import (
	"fmt"
	"math"
)

func main(){
	cir := new(Circle);
	cir.rad = 10;
	calc_area(cir);

	square := new(Square);
	square.a = 100;
	calc_area(square);

	rect := new(Rect);
	rect.a = 10;
	rect.b = 50;
	calc_area(rect);
}

// common interface for all shapes
type Shape interface {
	area() float32
}

type Circle struct {
	rad float32
}

// just for convenience square and rectangle are inverted 
// i.e. a rectangle is a square which is probably not that 
// right from purist OO standpoint but makes the code more clear 
type Square struct {
	a float32
}

// rectangle is a square with additional piece of information 
// regarding side
type Rect struct {
	Square
	b float32
}


// 'polymorphic' methods in action
func (this *Circle) area() float32{
	return math.Pi * this.rad * this.rad;
}

func (this *Square) area() float32 {
	return this.a * this.a;
}

func (this *Rect) area() float32 {
	return this.a * this.b;
}

func calc_area(this Shape){
	fmt.Println(this.area());
}


This code produces the following output: