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.