OdeToCode IC Logo

Composition over Inheritance in Go

Thursday, January 3, 2019

I’ve always admired languages that make composition as easy as inheritance. Groovy, with its @Delegate, is one example from years ago. These days I’ve been working a bit with Go. In Go, the composition features are so good you can achieve everything you need using compositional thinking.

We’ll use waterfowl as a silly example. Here’s a duck in Go.

type Duck struct {
    ID   int64
    Name string
}

I can also define a method on the Duck struct.

func (d *Duck) Eat() {
    fmt.Printf("Duck %s eats!\n", d.Name)
}

Later I might need a Mallard. A Mallard is very much like a Duck, but with a Mallard I need to track the color, too. Go doesn’t have inheritance, but embedding is elegant.

type Mallard struct {
    Duck
    Color string
}

Given a Mallard, I can reach into the ID and Name properties, or refer directly to the embedded Duck.

duck := new(Duck)
duck.ID = 1
duck.Name = "Pickles"

mallard := new(Mallard)
mallard.Color = "Green"

// copy info:
mallard.Name = duck.Name
mallard.ID = duck.ID
     
// or even better: 
mallard.Duck = *duck

And yes, I can define a method for Mallards.

func (m *Mallard) Sleep() {
    fmt.Printf("Mallard %s sleeps!\n", m.Name)
}

A mallard can now both eat and sleep.

    mallard.Eat()
    mallard.Sleep()

// Duck Pickles eats!
// Mallard Pickles sleeps!

The end result looks like inheritance, if you wear object-oriented glasses, but the mindset is entirely composition.


Comments
Gravatar Jonathan Allen Thursday, January 3, 2019
Inheritance, at its core, is just syntactic sugar for composition plus method forwarding. All they did was implement inheritance in a clumsy fashion because... I don't know why.
Gravatar Michael Carter Thursday, January 3, 2019
So mallard.Eat() is essentially mallard.Duck.Eat()? And is this only because you "embedded" the type Duck with no name? Otherwise a mallard could do everything a string or int64 does. Also, what happens if you embed a Bird type that also has a function Eat(). Does the compiler complain about ambiguity? I always wonder why language designers choose syntax that is different from existing languages.
Anoni Wednesday, January 16, 2019
Can you write a few words how Go handles the diamond problem ?
Allen Wednesday, January 23, 2019
In Go you get a compile time "ambiguous selector' error if you have a diamond problem and you don't explicitly select an embedded function or field. https://joaodlf.com/go-the-diamond-problem.html
Comments are closed.