Sling Academy
Home/Golang/Embedded Structs in Go: Achieving Composition Over Inheritance

Embedded Structs in Go: Achieving Composition Over Inheritance

Last updated: November 26, 2024

Introduction to Embedded Structs in Go

In the Go programming language, the concept of composition is preferred over inheritance. Instead of creating complex hierarchies, Go offers the feature of embedded structs to promote composition, enabling developers to build components that are more modular and maintainable.

Basic Example of Embedded Structs

Let's start with a simple example to demonstrate embedded structs in Go.

package main

import (
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person
    EmployeeID string
}

func main() {
    e := Employee{
        Person:     Person{Name: "John Doe", Age: 30},
        EmployeeID: "E12345",
    }
    fmt.Println("Name:", e.Name)         // Accessing the embedded struct field directly
    fmt.Println("Age:", e.Age)           // Accessing the embedded struct field directly
    fmt.Println("EmployeeID:", e.EmployeeID)
}

In this basic example, the Employee struct embeds the Person struct. Notice how you can easily access the fields of Person directly using an Employee instance.

Intermediate Example: Method promotion

An interesting feature of embedded structs is method promotion. Methods on embedded structs are promoted to the embedding struct.

package main

import (
    "fmt"
)

type Person struct {
    Name string
    Age  int
}

func (p Person) GetDetails() string {
    return fmt.Sprintf("Name: %s, Age: %d", p.Name, p.Age)
}

type Employee struct {
    Person
    EmployeeID string
}

func main() {
    e := Employee{
        Person:     Person{Name: "Jane Smith", Age: 28},
        EmployeeID: "E56789",
    }
    fmt.Println(e.GetDetails()) // Accessing Person's method directly from Employee
}

In this example, note how the GetDetails method defined on the Person struct is promoted to Employee, enabling us to call it directly on Employee instances.

Advanced Example: Struct Embedding with Interfaces

Embedding can also be beneficial when working with interfaces. Let’s see an example where struct embedding offers more power when interfacing different components.

package main

import (
    "fmt"
)

type Describer interface {
    Describe() string
}

type Animal struct {
    Specie string
}

func (a Animal) Describe() string {
    return "This is a " + a.Specie
}

type Dog struct {
    Animal
    Breed string
}

func main() {
    var d Describer = Dog{
        Animal: Animal{Specie: "Mammal"},
        Breed:  "Golden Retriever",
    }
    fmt.Println(d.Describe())
}

Here, the Dog struct embeds Animal which implements the Describe method from the Describer interface. Therefore, Dog automatically satisfies the Describer interface. This showcases how embedded structs can simplify type hierarchies in Go.

Conclusion

Using embedded structs in Go encourages clean design and composition over inheritance. This approach enhances reusability and flexibility, making our applications easier to maintain. As seen from the examples above, embedding structs and promoting methods and interfaces enhance readability and the organization of Go code.

Next Article: Anonymous Structs in Go: Quick Data Structures for Temporary Use

Previous Article: Using Struct Tags for JSON, XML, and Database Interactions in Go

Series: Structs and Interfaces in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant