OOPs in Go — The Simple Way
Go isn’t our typical object-oriented language like java, c++, python — and that’s by design. It focuses on simplicity, clarity, and minimalism. But it still supports the OOPs principle — just in its own way.
Most of us might have studied objects in our early programming days — something that has state (data) and can perform actions (behavior). And classes are simply the blueprint used to create those objects.
In Go, instead of classes. We use structs to mimic that behavior
🔧 Structs in Go — The OOPs Building Block
Go doesn’t use classes like other OOP languages. Instead, it uses structs — the same concept you may have seen in C or C++. But in Go, structs are more powerful. You can attach methods (method receivers) to them. That means your struct isn’t just a data holder — it can define behavior too!
// Animal struct
type Animal struct {
Name string
Age int
Habitat string
}
/* we dont have by default constrcutor, but we can define one using regular method */
func NewAnimal(name string, age int, habitat string) *Animal {
return &Animal{
Name: name,
Age: age,
Habitat: habitat,
}
}
Go removes the clutter of OOP by skipping constructors, destructors, and even traditional access modifiers. Instead, Go prefers composition over inheritance — which means we build things by combining smaller parts instead of relying on rigid parent-child relationships.
Now, let’s discuss each principle of OOPs with code snippets in detail. I've also implemented the whole example trying to cover basics , please check this out Zoo management System
Encapsulation — Who can touch my data?
Let’s first understand what “encapsulation” really means.
Imagine a doctor encapsulates bitter medicine inside a pill. You swallow the pill without tasting the bitterness. In programming, it’s the same idea — we wrap up complex things so users only see what they need. This helps us to keeps things simple.
Here’s why we Encapsulate code
How Go Implements Encapsulation
Now here’s the cool part — Go keeps it extremely simple. Instead of keywords like private or public, it just uses capitalization:
Abstraction — What do I want to expose?
Abstraction - showing the only important stuff, hiding the rest,
for eg. when we press brakes while driving a car, we dont care how the breaks are mechanically working. That’s abstraction: You use something without needing to know its inner details.
Why Do We Need Abstraction?
How is Abstraction Done in Go?
Go does'nt use the traditionals abstract classes - instead, it provides the interfaces.
// defining an inteface( function name only, no implementation)
type Animal interface{
Speak() string
}
// we dont use a keyword to impelement interface, just implement all function
type Dog struct{}
func(d Dog) Speak() string{
return "woof woof"
}
Similarly, we can create other types with their own implementation of Speak() function
Inheritance (Is-A) , Via Composition (Has-A)
In traditional OOP, inheritance means creating a new new class that automatically gets properties and behaviour from the parent class. I
But, go does'nt support inheritance (due to tight coupling of code). Instead it promotes, Composition - building complex types, by emebding the smaller and reusable types
Why We Use Composition
How to Use Compostion
Unlinke the traditional OOP, we dont use extend here. We just embed the parent type .
type Entity struct {
ID int
Name string
}
type Product struct {
Entity // embedded struct
Price float64
}
Polymorphism — Many Forms
In simple terms one interface, many implementations.
it allows different types to be treated as the same interface, even though they may have different implementations. In other words, If multiple structs implement the same interface, you can write code that works on the interface — not the specific struct —and it will work with any type that follows that behavior.
// common interface
type Notifier interface {
Notify(message string)
}
// multiple types implementing the same interface
type EmailNotifier struct{}
func (e EmailNotifier) Notify(message string) {
fmt.Println("Sending Email:", message)
}
type SMSNotifier struct{}
func (s SMSNotifier) Notify(message string) {
fmt.Println("Sending SMS:", message)
}
// using polymorphism
func SendAlert(n Notifier) {
n.Notify("System Down!")
}
// calling same function for different types
func main() {
var n Notifier
n = EmailNotifier{}
SendAlert(n) // Sending Email: System Down!
n = SMSNotifier{}
SendAlert(n) // Sending SMS: System Down!
}
Benefits of Using Polymorphism
Software Developer | ReactJs | NodeJS | React Native
1moI'm happy to join