Menu
Coddy logo textTech

Why Go Has No Inheritance

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

Traditional object-oriented languages like Java and C++ use inheritance to share code between types. A child class extends a parent class, inheriting all its fields and methods. Go deliberately omits this feature.

Inheritance creates tight coupling between types. When a parent class changes, all child classes are affected.

Deep inheritance hierarchies become difficult to understand and maintain. The "fragile base class problem" occurs when modifications to a base class unexpectedly break derived classes.

Go's designers chose a different path: composition over inheritance. Instead of saying "a Dog is an Animal," Go encourages you to say "a Dog has Animal-like behaviors." This subtle shift leads to more flexible, maintainable code.

Go achieves code reuse through two mechanisms you've already learned:

  • Interfaces define behavior contracts without implementation details
  • Struct embedding allows types to include other types and reuse their methods

Consider this comparison. In traditional OOP, you might write class Dog extends Animal. In Go, you embed an Animal struct inside Dog and implement shared interfaces. The result is similar functionality with looser coupling between types.

This chapter explores struct embedding in depth, showing how Go achieves the benefits of inheritance without its drawbacks.

challenge icon

Challenge

Easy

Let's build a notification system that demonstrates Go's composition approach instead of inheritance. You'll create types that share behavior through interfaces and struct embedding rather than class hierarchies.

You'll organize your code across three files:

  • notifier.go: Define a Notifier interface with a single method Notify(message string) string. Also create a BaseNotifier struct with a Name field that will be embedded by other types. Give BaseNotifier a method called Format(message string) string that returns the message prefixed with the notifier's name in brackets.
  • channels.go: Create two notification channel types that embed BaseNotifier and implement the Notifier interface:
    • EmailNotifier with an additional Address field
    • SMSNotifier with an additional Phone field
    Each type's Notify method should use the embedded Format method and include its specific channel information in the output.
  • main.go: Create a function called SendAlert that accepts any Notifier and a message, then returns the result of calling Notify. Read the notification details from input, create both types of notifiers, and demonstrate how they can be used interchangeably through the interface.

The following inputs will be provided:

  • Line 1: Notifier name for email
  • Line 2: Email address
  • Line 3: Notifier name for SMS
  • Line 4: Phone number
  • Line 5: Alert message

Your Format method on BaseNotifier should return:

[Name] message

Your Notify methods should return:

  • EmailNotifier: Email to [Address]: [formatted message]
  • SMSNotifier: SMS to [Phone]: [formatted message]

For example, given Alerts, user@mail.com, Urgent, 555-1234, and Server down, your output should be:

Email to user@mail.com: [Alerts] Server down
SMS to 555-1234: [Urgent] Server down

Notice how both notifier types reuse the Format method from BaseNotifier through embedding, while each provides its own Notify implementation. The SendAlert function works with any Notifier without knowing the concrete type—this is composition over inheritance in action.

Cheat sheet

Go uses composition over inheritance to share code between types, avoiding the tight coupling and fragile base class problems of traditional object-oriented inheritance.

Instead of class hierarchies, Go achieves code reuse through:

  • Interfaces - define behavior contracts without implementation details
  • Struct embedding - allows types to include other types and reuse their methods

Rather than saying "a Dog is an Animal" (inheritance), Go encourages "a Dog has Animal-like behaviors" (composition).

Struct Embedding Example

Define a base struct that will be embedded:

type BaseNotifier struct {
    Name string
}

func (b BaseNotifier) Format(message string) string {
    return "[" + b.Name + "] " + message
}

Embed the base struct in other types to reuse its fields and methods:

type EmailNotifier struct {
    BaseNotifier  // embedded struct
    Address string
}

func (e EmailNotifier) Notify(message string) string {
    // Can call embedded Format method directly
    return "Email to " + e.Address + ": " + e.Format(message)
}

Define an interface that multiple types can implement:

type Notifier interface {
    Notify(message string) string
}

Use the interface to work with different concrete types interchangeably:

func SendAlert(n Notifier, message string) string {
    return n.Notify(message)
}

This approach provides similar functionality to inheritance but with looser coupling between types.

Try it yourself

package main

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

// SendAlert accepts any Notifier and a message, returns the result of calling Notify
// TODO: Implement the SendAlert function

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Read email notifier name
	scanner.Scan()
	emailName := scanner.Text()
	
	// Read email address
	scanner.Scan()
	emailAddress := scanner.Text()
	
	// Read SMS notifier name
	scanner.Scan()
	smsName := scanner.Text()
	
	// Read phone number
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Read alert message
	scanner.Scan()
	alertMessage := scanner.Text()
	
	// TODO: Create an EmailNotifier with emailName and emailAddress
	
	// TODO: Create an SMSNotifier with smsName and phoneNumber
	
	// TODO: Use SendAlert to send the alertMessage through both notifiers
	// and print the results
	
	// Suppress unused variable warnings (remove these when you use the variables)
	_ = emailName
	_ = emailAddress
	_ = smsName
	_ = phoneNumber
	_ = alertMessage
}
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