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.