Introduction
One of the first things new Go programmers hear is:
"Go prefers composition over inheritance."
If you come from Java, C++, or C#, this may sound strange because those languages rely heavily on inheritance. Let's explore what this means and why Go was designed this way.
What is Inheritance?
In object-oriented languages, a class can inherit from another class.
Animal ├── Dog ├── Cat └── Bird
The parent class contains common functionality that every child receives automatically.
class Animal {
void Eat() {
System.out.println("Eating");
}
}
class Dog extends Animal {
void Bark() {
System.out.println("Woof");
}
}
Since Dog inherits from Animal, every Dog automatically gets:
- Eat()
- Variables
- Methods
without rewriting any code.
Dog dog = new Dog();
dog.Eat();
dog.Bark();
A Dog is an Animal.
This is called an is-a relationship.
Why Inheritance Became Popular
Suppose you have:
- Dog
- Cat
- Horse
- Lion
Every one of them eats.
Instead of writing:
Eat()
four different times, you write it once inside Animal.
Inheritance reduces duplicated code.
But Inheritance Has Problems
Years later, your Animal class grows.
Animal ------------ Eat() Sleep() Walk() Run() Swim() Fly()
Suddenly problems appear.
- Penguins cannot fly.
- Fish don't walk.
- Snakes don't have legs.
- Bats fly but aren't birds.
You begin overriding methods.
class Penguin extends Bird {
void Fly() {
throw new Exception("Can't fly");
}
}
Not ideal.
Eventually your inheritance tree becomes huge.
Animal | Mammal | Canine | DomesticDog | GermanShepherd
Now changing one class can accidentally affect dozens of other classes.
Go Chose a Different Philosophy
Instead of saying:
Dog IS an Animal
Go says:
Dog HAS behavior.
Instead of inheriting functionality, you build larger objects using smaller objects.
Think LEGO bricks.
What is Composition?
Composition means one object contains another object.
Instead of:
Dog inherits Animal
Go prefers:
Dog └── Animal
The relationship changes from:
- is-a
to:
- has-a
Real-Life Example
A car isn't an engine.
A car has:
- Engine
- Seats
- Doors
- Wheels
- Steering wheel
Those parts work together.
That is composition.
Composition in Go
type Animal struct {
Name string
}
Now create a Dog.
type Dog struct {
Animal
}
Notice something.
There is no:
extends
There is no:
inherits
Dog simply contains an Animal.
Creating a Dog
dog := Dog{
Animal: Animal{
Name: "Buddy",
},
}
Memory looks like this:
Dog ------------------- Animal Name = "Buddy"
Embedded Structs
Go has a feature called embedding.
type Dog struct {
Animal
}
Instead of writing:
dog.Animal.Name
you can simply write:
dog.Name
Go automatically forwards the request.
It feels similar to inheritance but is actually composition.
Another Example
type Engine struct {
Horsepower int
}
type Car struct {
Engine
Brand string
}
Memory:
Car
----------------
Brand
Engine
Horsepower
You can access:
car.Horsepower
even though Horsepower belongs to Engine.
Why Composition is Flexible
Imagine building an electric car.
Instead of:
Vehicle | Car | ElectricCar
you simply assemble pieces.
ElectricCar Battery Motor GPS AirConditioner Radio
Need GPS? Add GPS.
Need Radio? Add Radio.
Each component is independent.
Small Reusable Components
Suppose you have:
- Logger
- Database
- Configuration
- NetworkClient
Build a server by combining them.
Server ---------------- Logger Database Configuration NetworkClient
Rather than giant inheritance trees, Go encourages assembling software from reusable pieces.
Interfaces Make Composition Even Stronger
Go asks:
Can this object perform this behavior?
instead of:
Is this object an Animal?
type Speaker interface {
Speak()
}
Dog:
func (d Dog) Speak() {
fmt.Println("Woof")
}
Cat:
func (c Cat) Speak() {
fmt.Println("Meow")
}
Robot:
func (r Robot) Speak() {
fmt.Println("Beep")
}
None inherit from each other.
Yet they all satisfy the Speaker interface because they implement Speak().
This is called a can-do relationship.
Why Go Developers Love Composition
- Small focused types
- Reusable building blocks
- Loose coupling
- Easier testing
- Simpler architecture
- No deep inheritance trees
Real World Analogy
Think about a computer.
A computer doesn't inherit from a CPU.
It is built from components.
Computer ├── CPU ├── RAM ├── SSD ├── GPU ├── Motherboard └── Power Supply
Each part has one responsibility.
If you upgrade the GPU, you don't redesign the whole computer.
That is composition.
Inheritance vs Composition
| Inheritance | Composition |
|---|---|
| is-a relationship | has-a relationship |
| Deep class hierarchies | Small reusable pieces |
| Tight coupling | Loose coupling |
| Parent changes affect children | Independent components |
| Common in Java, C++, C# | Core philosophy of Go |
Final Thoughts
Go encourages programmers to think less about building large inheritance trees and more about assembling software from small, reusable components.
Instead of asking:
"What class should inherit from this?"
Go programmers usually ask:
"What small pieces can I combine to build this?"
That simple shift in thinking is one of the reasons Go code often feels clean, modular, and easy to maintain.