Menu
Coddy logo textTech

Embedding vs Aggregation

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 38 of 107.

You've learned that embedding promotes fields and methods to the outer struct. But Go also supports aggregation, where you include another struct as a named field. Understanding when to use each approach is key to good design.

With embedding, you omit the field name, and the inner type's members become directly accessible:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // embedding - no field name
}

d := Document{}
d.Write()  // direct access

With aggregation, you give the field an explicit name, requiring that name to access the inner type:

type Document struct {
    writer Writer  // aggregation - named field
}

d := Document{writer: Writer{}}
d.writer.Write()  // must use field name

The key difference is the relationship you're expressing. Embedding suggests an "is-a" relationship where the outer type takes on the identity of the inner type. Aggregation suggests a "has-a" relationship where the outer type merely contains the inner type.

Use embedding when you want the outer type to satisfy interfaces implemented by the inner type, or when direct method access makes the API cleaner. Use aggregation when the inner type is an implementation detail that shouldn't be exposed, or when you want explicit control over how the inner type is accessed.

challenge icon

Challenge

Easy

Let's build a report generation system that demonstrates when to use embedding versus aggregation. You'll create a system where some components should be directly accessible (embedding) while others should remain internal implementation details (aggregation).

You'll organize your code across three files:

  • formatter.go: Create a Formatter struct with a Style field (string) and a Format(text string) string method that returns the text wrapped with the style, like [Style] text [/Style]. This will be an internal helper that shouldn't be directly exposed.
  • report.go: Create two structs that handle report generation differently:
    • A Metadata struct with Author and Date fields—this represents public information that should be directly accessible
    • A Report struct with a Title field that embeds Metadata (for direct access to author and date) but uses aggregation for Formatter (as an internal implementation detail with a named field formatter)
    Add a Generate() string method to Report that uses the internal formatter to format the title and includes the metadata.
  • main.go: Read report details from input, create a Report with both embedded metadata and an aggregated formatter. Demonstrate the difference by accessing the author directly on the report (through embedding) and then generating the formatted report (which uses the hidden formatter internally).

The following inputs will be provided:

  • Line 1: Report title
  • Line 2: Author name
  • Line 3: Date
  • Line 4: Formatter style

Your Format method should return:

[Style] text [/Style]

Your Generate method should return:

[formatted title]
Author: [Author], Date: [Date]

In your main file, print three lines:

  1. The author accessed directly on the Report (demonstrating embedding promotes the field)
  2. The date accessed through the embedded type name (report.Metadata.Date)
  3. The result of calling Generate()

For example, given Quarterly Results, Jane Doe, 2024-01-15, and BOLD, your output should be:

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

Notice how Author and Date are accessible directly on the Report through embedding, while the formatter remains hidden—you can't call report.Format() directly because it's aggregated with a named field. This demonstrates the "is-a" versus "has-a" relationship in practice.

Cheat sheet

Go supports two ways to include one struct within another: embedding and aggregation.

Embedding omits the field name, promoting the inner type's fields and methods to the outer struct:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // embedding - no field name
}

d := Document{}
d.Write()  // direct access to embedded method

Aggregation uses an explicit field name, requiring that name to access the inner type:

type Document struct {
    writer Writer  // aggregation - named field
}

d := Document{writer: Writer{}}
d.writer.Write()  // must use field name

When to use each:

  • Embedding: Expresses an "is-a" relationship. Use when the outer type should satisfy interfaces of the inner type, or when direct method access improves the API.
  • Aggregation: Expresses a "has-a" relationship. Use when the inner type is an implementation detail that shouldn't be exposed, or when you want explicit control over access.

With embedding, you can still access fields through the embedded type name if needed:

type Metadata struct {
    Author string
}

type Report struct {
    Metadata  // embedded
}

r := Report{Metadata: Metadata{Author: "Jane"}}
r.Author              // direct access via promotion
r.Metadata.Author     // explicit access via type name

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: Create a Metadata struct with the author and date
	
	// TODO: Create a Formatter with the given style
	
	// TODO: Create a Report that embeds Metadata and aggregates Formatter
	
	// TODO: Print the author accessed directly on the Report (embedding promotes the field)
	
	// TODO: Print the date accessed through the embedded type name (report.Metadata.Date)
	
	// TODO: Print the result of calling Generate()
	
	// Suppress unused variable warnings (remove these when you use the variables)
	_ = title
	_ = author
	_ = date
	_ = style
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming