POO: Encapsulamento e Polimorfismo em Go (Golang): Um Guia Detalhado
What is it and why? (a real-life analogy)
Imagine you are assembling a robot from a LEGO set. You have a motor, a light sensor, and wheels. You connect them with wires, and the robot moves, avoiding obstacles. Now imagine you are an engineer at a factory designing millions of such robots. You can't assemble them all by hand every time. You create a blueprint (class), according to which a machine stamps out parts (objects).
Encapsulation is like the robot's casing. Inside the casing, complex wires, batteries, and microchips are hidden. The user (or another robot) does not see this complexity. They only see the buttons on the remote control: "Forward", "Left", "Pick up object". If the user presses the "Forward" button, the robot moves. They don't need to know which current goes to the motor or how the gearbox works. Encapsulation hides the internal structure and provides access only through safe methods (buttons).
Polymorphism is like a universal remote control. You have three different robots: a vacuum cleaner robot, a robot dog, and a robotic arm. Each has its own "language" of commands. But you want them all to understand the "Stop" command. For the vacuum cleaner robot, "Stop" means turning off the motor. For the robot dog, it means sitting down and freezing. For the robotic arm, it means stopping movement and locking the grip. Polymorphism allows different objects to react differently to the same command. You say "Stop", and each robot does what it is supposed to do.
In programming, encapsulation protects data from accidental corruption, and polymorphism makes code flexible and extensible. Without them, your program will look like a bunch of wires sticking out of a robot: scary and dangerous.
Theory: how it works internally
Encapsulation: three levels of protection
Go does not have classical classes like Java or Python. Instead, structures (struct) and methods are used. Encapsulation in Go is built at the package level.
- Private: the name of a field or method starts with a lowercase letter. It is accessible only within the same package. This is like the internal wires of a robot — only the engineer at the factory sees them.
- Public: the name starts with an uppercase letter. It is accessible from any other package. This is like the buttons on the remote control.
Example: you have a package robot. Inside it, there is a structure Robot with a field speed (lowercase letter — private) and a method SetSpeed (uppercase letter — public). Another programmer using your package cannot directly write myRobot.speed = 100. They must call myRobot.SetSpeed(100). This is encapsulation: access control.
Polymorphism: interfaces — the key to flexibility
Polymorphism in Go is implemented through interfaces. An interface is a contract. It says: "Any type that has a method Speak() string can be considered an implementation of this interface."
In Go, you don't need to explicitly write implements (like in Java). If a structure has all the methods required by the interface, it automatically implements it. This is called duck typing: "If it walks like a duck and quacks like a duck, then it is a duck."
When you write a function that takes an interface, you can pass it any concrete type that implements that interface. The function calls the method, and each type executes it in its own way.
Interfaces in Go are not about "inheritance", but about "behavior". You don't ask the object "Who are you?", you ask "What can you do?".
Syntax in Go (Golang): all forms and variants
1. Structures and methods (the basis of encapsulation)
// Defining a structure (analogous to a class)
type Car struct {
// Private field (accessible only within the main package)
speed int
// Public field (accessible from everywhere)
Brand string
}
// Method with a "receiver" — this is a way to bind a function to a structure
// (c *Car) — pointer receiver. Allows modifying the structure's fields.
func (c *Car) SetSpeed(s int) {
if s < 0 {
s = 0
}
if s > 200 {
s = 200
}
c.speed = s // Changing a private field through a public method
}
// Getter for the private field
func (c *Car) GetSpeed() int {
return c.speed
}
// Method without modifying the structure (value receiver)
func (c Car) Honk() string {
return "Beep! Beep!"
}
2. Interfaces (the foundation of polymorphism)
// Defining an interface
type Speaker interface {
Speak() string // Contract: any type that has a Speak method implements this interface
}
// Structure Dog
type Dog struct {
Name string
}
// Dog implements the Speaker interface
func (d Dog) Speak() string {
return "Woof! I am " + d.Name
}
// Structure Cat
type Cat struct {
Name string
}
// Cat also implements Speaker
func (c Cat) Speak() string {
return "Meow! I am " + c.Name
}
// Function that takes an interface
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
3. Empty interface (interface{})
In Go 1.18+, the alias any appeared. An empty interface can hold a value of any type. It is used when the type is not known in advance.
func PrintAnything(a any) {
fmt.Printf("Value: %v, Type: %T
", a, a)
}
// Usage
PrintAnything(42)
PrintAnything("Hello")
PrintAnything(3.14)
4. Embedding — composition over inheritance
Go does not support classical inheritance. Instead, embedding one structure into another is used. This provides access to the fields and methods of the embedded structure.