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

package main

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.
  • EnmainEn 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

package main

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.
  • EnmainEn 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ísticaHerencia clásicaEnfoque de Go
Reutilización de códigoMediante herenciaMediante composición (incrustar estructuras)
PolimorfismoMediante herencia y sobrescritura de métodosMediante la implementación de interfaces
RelaciónRelación «es un» (is-a)Relación «tiene un» (has-a) o «implementa»
FlexibilidadRelación de herencia fijaComposición en tiempo de ejecución

Simulación completa de herencia:

Ejemplo

package main

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