Composition Instead of Inheritance - OOP in Go

Welcome to tutorial no. 27 in Golang tutorial series.

Go does not support inheritance, however, it does support composition. The generic definition of composition is “put together”. One example of composition is a car. A car is composed of wheels, an engine and various other parts.

Composition by embedding structs

Composition can be achieved in Go is by embedding one struct type into another.

A blog post is a perfect example of composition. Each blog post has a title, content and author information. This can be perfectly represented using composition. In the next steps of this tutorial, we will learn how this is done.

Let’s first create the author struct.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7type author struct {
 8	firstName string
 9	lastName  string
10	bio       string
11}
12
13func (a author) fullName() string {
14	return fmt.Sprintf("%s %s", a.firstName, a.lastName)
15}

In the above code snippet, we have created a author struct with fields firstName, lastName and bio. We have also added a method fullName() with the author as receiver type and this returns the full name of the author.

The next step would be to create the blogPost struct.

 1type blogPost struct {
 2	title     string
 3	content   string
 4	author
 5}
 6
 7func (b blogPost) details() {
 8	fmt.Println("Title: ", b.title)
 9	fmt.Println("Content: ", b.content)
10	fmt.Println("Author: ", b.author.fullName())
11	fmt.Println("Bio: ", b.author.bio)
12}

The blogPost struct has fields title, content. It also has an embedded anonymous field author. This field denotes that blogPost struct is composed of author. Now blogPost struct has access to all the fields and methods of the author struct. We have also added details() method to the blogPost struct which prints the title, content, fullName and bio of the author.

Whenever one struct field is embedded in another, Go gives us the option to access the embedded fields as if they were part of the outer struct. This means that p.author.fullName() in line no. 11 of the above code can be replaced with p.fullName(). Hence the details() method can be rewritten as below,

1func (p blogPost) details() {
2	fmt.Println("Title: ", p.title)
3	fmt.Println("Content: ", p.content)
4	fmt.Println("Author: ", p.fullName())
5	fmt.Println("Bio: ", p.bio)
6}

Now that we have the author and the blogPost structs ready, let’s finish this program by creating a blog post.

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7type author struct {
 8	firstName string
 9	lastName  string
10	bio       string
11}
12
13func (a author) fullName() string {
14	return fmt.Sprintf("%s %s", a.firstName, a.lastName)
15}
16
17type blogPost struct {
18	title   string
19	content string
20	author
21}
22
23func (b blogPost) details() {
24	fmt.Println("Title: ", b.title)
25	fmt.Println("Content: ", b.content)
26	fmt.Println("Author: ", b.fullName())
27	fmt.Println("Bio: ", b.bio)
28}
29
30func main() {
31	author1 := author{
32		"Naveen",
33		"Ramanathan",
34		"Golang Enthusiast",
35	}
36	blogPost1 := blogPost{
37		"Inheritance in Go",
38		"Go supports composition instead of inheritance",
39		author1,
40	}
41	blogPost1.details()
42}

Run in playground

The main function of the program above creates a new author in line no. 31. A new post is created in line no. 36 by embedding author1. This program prints,

Title:  Inheritance in Go
Content:  Go supports composition instead of inheritance
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast

Embedding slice of structs

We can take this example one step further and create a website using a slice of blog posts :).

Let’s define the website struct first. Please add the following code above the main function of the existing program and run it.

 1type website struct {
 2        []blogPost
 3}
 4func (w website) contents() {  
 5    fmt.Println("Contents of Website\n")
 6    for _, v := range w.blogPosts {
 7        v.details()
 8        fmt.Println()
 9    }
10}

When you run the program above after adding the above code, the compiler will complain about the following error,

main.go:31:9: syntax error: unexpected [, expecting field name or embedded type

This error points to the embedded slice of structs []blogPost. The reason is that it is not possible to anonymously embed a slice. A field name is required. So let’s fix this error and make the compiler happy.

1type website struct {
2        blogPosts []blogPost
3}

I have added the field blogPosts which is a slice []blogPosts.

Now let’s modify the main function and create a few posts for our new website.

The complete program after modifying the main function is provided below,

 1package main
 2
 3import (
 4	"fmt"
 5)
 6
 7type author struct {
 8	firstName string
 9	lastName  string
10	bio       string
11}
12
13func (a author) fullName() string {
14	return fmt.Sprintf("%s %s", a.firstName, a.lastName)
15}
16
17type blogPost struct {
18	title   string
19	content string
20	author
21}
22
23func (p blogPost) details() {
24	fmt.Println("Title: ", p.title)
25	fmt.Println("Content: ", p.content)
26	fmt.Println("Author: ", p.fullName())
27	fmt.Println("Bio: ", p.bio)
28}
29
30type website struct {
31	blogPosts []blogPost
32}
33
34func (w website) contents() {
35	fmt.Println("Contents of Website\n")
36	for _, v := range w.blogPosts {
37		v.details()
38		fmt.Println()
39	}
40}
41
42func main() {
43	author1 := author{
44		"Naveen",
45		"Ramanathan",
46		"Golang Enthusiast",
47	}
48	blogPost1 := blogPost{
49		"Inheritance in Go",
50		"Go supports composition instead of inheritance",
51		author1,
52	}
53	blogPost2 := blogPost{
54		"Struct instead of Classes in Go",
55		"Go does not support classes but methods can be added to structs",
56		author1,
57	}
58	blogPost3 := blogPost{
59		"Concurrency",
60		"Go is a concurrent language and not a parallel one",
61		author1,
62	}
63	w := website{
64		blogPosts: []blogPost{blogPost1, blogPost2, blogPost3},
65	}
66	w.contents()
67}

Run in playground

In the main function above, we have created an author author1 and three posts post1, post2 and post3. Finally, we have created the website w in line no. 62 by embedding these 3 posts and displayed the contents in the next line.

This program will output,

Contents of Website

Title:  Inheritance in Go
Content:  Go supports composition instead of inheritance
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast

Title:  Struct instead of Classes in Go
Content:  Go does not support classes but methods can be added to structs
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast

Title:  Concurrency
Content:  Go is a concurrent language and not a parallel one
Author:  Naveen Ramanathan
Bio:  Golang Enthusiast

This brings us to the end of this tutorial. I hope you liked it. Please leave your feedback and comments. Please consider sharing this tutorial on twitter and LinkedIn. Have a good day.

Next tutorial - Polymorphism