How to: Using Composition over Inheritance

Mathis Engelbart
applike
Published in
3 min readMar 18, 2019

Written by Mathis Engelbart and Marc Lange

In traditional object oriented languages like Java or C++ developers are very used to implement a type hierarchy. There is no way to do so in Go, because it does not support inheritance by design. Go’s preferred way to reuse code is called composition. In Go you can use interface- and struct-embedding to use composition, but conceptually there is no big difference to using a field inside a struct. Hence composition in Go is actually not a new concept, and it is easily possible to use composition in other languages to reuse code.

Why Would We Not Use Subclasses?

Subtyping can break encapsulation and lead to tightly coupled classes. While there are cases, where types can be expressed as a hierarchy in a tree, it is also easy to build a tree of types, which superficially seem to share some properties, but actually are very different things.

As an example suppose you want to simulate different types of animals. You can have a base class called Animal:

And you can implement a subtype for dogs:

Or a subclass for fish:

Here we have to override the run function, to prevent the fish from actually being able to run. However, there are a lot of animals to implement and a lot of them are able to run, some of them in similar ways. So instead of inheriting the method run, we can use a Runner interface to give our Dog the property of being able to run.

This way we can remove the run method from all the other animals, which cannot run. This is just one example, where composition is preferable over inheritance, others may even include more complex bugs. This will be the case especially when algorithms are split over different classes.

The Specifics in Go: Embedding

In Go we can use embedding as a shortcut to compose new types. Implementing the example in go could look something like this:

The dog is very similar to the Java-Dog. It has a field called runner, which can be used as dog.runner.Run(10), whereas the cat embeds a Runner, an can thus be used as cat.Run(10). The function Run of the struct cat is called promoted from its Runner-implementation. In our example, the cats Runner will be a FourLegRunner, initialized in NewCat.

An important point here is, that embedding is not the same as inheritance. In Go you cannot use an Animal-struct and embed it in the Dog and then assume the Dog will behave like a subtype of Animal. The Dog will still be of type Dog and — unlike in Java — you cannot pass a Dog, where an Animal is expected. If you want some kind of polymorphism, you have to do it with interfaces instead.

Conclusion

Go does not support inheritance and encourages the use of composition for code reuse instead. There are pros and cons to the use of inheritance, Go chooses not to use inheritance at all. In most cases there will be a way to express your ideas using composition. If you need subtyping for polymorphism, you can instead use an interface and describe exactly the type you expect.

Got until here and would love to work with us on the next big mobile challenges? Check our open positions or drop us a line at jobs@applike.info

--

--