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.
OdeToCode by K. Scott Allen