Lesson 4
Composing Code in Go: A Beginner's Guide to Structs and Interfaces
Lesson Overview

Hello! Today, we're exploring a fundamental facet of Go: Composition. Composition is a design principle in Go that enables code reuse. Unlike languages that use inheritance, Go opts for Composition. This lesson aims to understand Composition and how it applies in Go.

Understanding Struct Buzzwords

Composition in Go allows for the construction of complex types using simpler ones. In Go's Composition, you frequently encounter terms like Embedding and Anonymous Fields. Embedding involves including one struct inside another, creating a parent-child relationship. Anonymous Fields are declared in a struct without a name, and their type becomes their name.

Go
1type Point struct { 2 X, Y float64 3} 4 5type Circle struct { 6 Point // Embedding Point struct into Circle 7 Radius float64 8}

In the example above, Point is an anonymous field in the Circle.

Working with Composition in Go: Part 1

Now, let's see Composition in action in Go, using structs:

Go
1package main 2import "fmt" 3 4// 'Person' struct 5type Person struct { 6 Name string 7 Age int 8} 9 10// 'Student' struct, embedding 'Person' 11type Student struct { 12 Person 13 Grade int 14} 15 16func main() { 17 john := Student{ 18 Person: Person{ 19 Name: "John Appleseed", 20 Age: 21, 21 }, 22 Grade: 12, 23 } 24 25 fmt.Println(john.Name) // Output: "John Appleseed" 26}

Here, the Person struct is embedded into the Student struct. We then directly access the Name field from the Student struct instance named john.

Working with Composition in Go: Part 2

Note that we can also access the Name like this:

Go
1fmt.Println(john.Person.Name)

In some cases, this can be useful. For example, if the "child" struct has a field named the same as one of the fields of the "parent" struct, it would be a proper way to access it. Consider the following example:

Go
1type School struct { 2 Name string 3} 4 5type Student struct { 6 Name string 7 School 8}

In this case, a student has an embedded school. Both Student and School has fields Name, so here is how we distinct them:

Go
1student.Name // accessing name of the student 2student.School.Name // accessing name of the student's school
Power of Composition: Extending and Overriding

Composition shines in situations where it's necessary to extend or override the functionality of the embedded type. Here, Person has a GetUp() method that is overridden for Student.

Go
1// 'Person' struct 2type Person struct { 3 Name string 4} 5 6func (p *Person) GetUp() { 7 fmt.Println(p.Name, "gets up.") 8} 9 10type Student struct { 11 Person 12 StudentID string 13} 14 15func (s *Student) GetUp() { 16 fmt.Println(s.Name, "the student gets up.") 17} 18 19func main() { 20 p := Person{Name: "Bob"} 21 s := Student{Person: Person{Name: "Alice"}, StudentID: "s123"} 22 23 p.GetUp() // Output: "Bob gets up." 24 s.GetUp() // Output: "Alice the student gets up." 25}
Lesson Summary and Next Steps

Bravo! We've journeyed through Go's Composition, understanding key terms such as Embedding and Anonymous Fields and implementing Composition in Go code. Prepare for exercises that will reinforce your learning and build confidence in using Composition in Go!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.