Herencia en Go
En la programación orientada a objetos (POO), la herencia es un mecanismo que permite a una clase (subclase) heredar propiedades y métodos de otra clase (superclase). Mediante la herencia, la subclase puede reutilizar el código de la superclase y puede extender o modificar su comportamiento sin modificar la superclase.
Go no es un lenguaje de programación orientado a objetos tradicional; no tiene los conceptos de clase y herencia.
Go utiliza estructuras (struct) e interfaces (interface) para implementar una funcionalidad similar.
La "herencia" en Go
Go no tiene los conceptos de clase (class) y herencia (inheritance) de los lenguajes orientados a objetos tradicionales, sino que logra una funcionalidad similar mediante la composición (composition) y las interfaces (interface).
1. Composición (Composition)
La composición es la forma principal de lograr la reutilización de código en Go. Al incrustar una estructura dentro de otra, la subestructura puede «heredar» los campos y métodos de la estructura padre.
Ejemplo
import "fmt"
// Estructura padre
type Animal struct {
Name string
}
// Método de la estructura padre
func (a *Animal) Speak() {
fmt.Println(a.Name, "says hello!")
}
// Subestructura
type Dog struct {
Animal // Incrusta la estructura Animal
Breed string
}
func main() {
dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}
dog.Speak() // Llama al método de la estructura padre
fmt.Println("Breed:", dog.Breed)
}
Explicación del código
Animales la estructura padre, contiene un campoNamey un métodoSpeak。Doges la subestructura, mediante la incrustación deAnimalla estructura, heredaAnimallos campos y métodos de esta.- En
mainEn la función, creamos unaDoginstancia, y llamamos alSpeakmétodo.
2. Interfaz (Interface)
La interfaz es la forma principal de implementar polimorfismo en Go. Al definir una interfaz, diferentes estructuras pueden implementar los mismos métodos, logrando así un comportamiento polimórfico similar a la herencia.
Código de ejemplo
Ejemplo
import "fmt"
// Define la interfaz
type Speaker interface {
Speak()
}
// Estructura padre
type Animal struct {
Name string
}
// Implementa el método de la interfaz
func (a *Animal) Speak() {
fmt.Println(a.Name, "says hello!")
}
// Subestructura
type Dog struct {
Animal
Breed string
}
func main() {
var speaker Speaker
dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}
speaker = &dog
speaker.Speak() // Llama al método a través de la interfaz
}
Explicación del código
Speakeres una interfaz, define unSpeakmétodo.Animalla estructura implementaSpeakerla interfaz.DogLa estructura, mediante la incrustación deAnimalla estructura, implementa indirectamenteSpeakerla interfaz.- En
mainEn la función, asignamosDogla instancia aSpeakerla interfaz, y a través de la interfaz llamamos alSpeakmétodo.
Diferencias entre Go y la herencia clásica
| Característica | Herencia clásica | Enfoque de Go |
|---|---|---|
| Reutilización de código | Mediante herencia | Mediante composición (incrustar estructuras) |
| Polimorfismo | Mediante herencia y sobrescritura de métodos | Mediante la implementación de interfaces |
| Relación | Relación «es un» (is-a) | Relación «tiene un» (has-a) o «implementa» |
| Flexibilidad | Relación de herencia fija | Composición en tiempo de ejecución |
Simulación completa de herencia:
Ejemplo
import "fmt"
// Clase base
type Vehicle struct {
Brand string
}
func (v *Vehicle) Start() {
fmt.Println(v.Brand, "started")
}
// Clase derivada
type Car struct {
Vehicle // Incrusta Vehicle
Model string
}
// Sobrescribe el método Start
func (c *Car) Start() {
fmt.Println(c.Brand, c.Model, "car started")
}
func main() {
v := Vehicle{Brand: "Toyota"}
c := Car{
Vehicle: Vehicle{Brand: "Honda"},
Model: "Civic",
}
v.Start() // Toyota started
c.Start() // Honda Civic car started
c.Vehicle.Start() // Honda started
}
Este diseño de Go evita muchos problemas de la herencia tradicional, como el problema de la clase base frágil, al tiempo que ofrece una mayor flexibilidad.
Otras extensiones