Menu
Coddy logo textTech

Promoción de métodos

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 36 de 107.

Al igual que la incrustación de structs promueve campos a la struct externa, Go también promueve métodos de los tipos incrustados. Esto significa que puedes llamar a los métodos definidos en la struct incrustada directamente en la struct externa.

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // embebido
}

Dado que Car incrusta Engine, el método Start() se promociona automáticamente:

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Motor arrancado
    fmt.Println(c.Engine.Start())  // también funciona
}

Esta promoción tiene una consecuencia importante para las interfaces. Si el tipo embebido satisface una interfaz, el tipo externo también la satisface automáticamente:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisface Starter a través de Engine
}

El tipo Car nunca implementa explícitamente Starter, pero satisface la interfaz porque su Engine embebido tiene el método requerido. Así es como Go logra la reutilización de comportamiento sin la herencia tradicional.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de reproductor de música que demuestre cómo los métodos de los tipos embebidos se promocionan al tipo externo, y cómo esto permite satisfacer interfaces a través de la composición.

Organizarás tu código en dos archivos:

  • audio.go: Crea los bloques de construcción para tu reproductor de música:
    • Una estructura AudioPlayer con un campo Brand (string) y un método Play() string que devuelva [Brand] playing audio
    • Una interfaz Playable que requiera un método Play() string
    • Una estructura Smartphone con un campo Model que embeba AudioPlayer
    • Una estructura Tablet con un campo Model que embeba AudioPlayer
  • main.go: Crea una función llamada StartPlayback que acepte cualquier Playable y devuelva el resultado de llamar a Play(). Lee la información del dispositivo desde la entrada, crea tanto un Smartphone como una Tablet, y demuestra que ambos satisfacen la interfaz Playable a través de su AudioPlayer embebido, a pesar de que ninguno de los tipos implementa explícitamente Play() por sí mismo.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre del modelo del Smartphone
  • Línea 2: Marca de audio del Smartphone
  • Línea 3: Nombre del modelo de la Tablet
  • Línea 4: Marca de audio de la Tablet

Para cada dispositivo, imprime su modelo seguido del resultado de pasarlo a StartPlayback.

Por ejemplo, dados iPhone 15, Apple Audio, iPad Pro, y Beats, tu salida debería ser:

iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audio

Observa cómo tanto Smartphone como Tablet pueden pasarse a StartPlayback porque el método Play() de AudioPlayer se promociona automáticamente a cada tipo externo, satisfaciendo la interfaz Playable sin ningún código adicional.

Hoja de referencia

Cuando una estructura (struct) incrusta otro tipo, los métodos del tipo incrustado se promueven automáticamente a la estructura externa:

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // incrustado
}

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // también funciona
}

Si un tipo incrustado satisface una interfaz, el tipo externo también la satisface automáticamente:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisface Starter a través de Engine
}

Esto permite la reutilización de comportamiento a través de la composición sin la herencia tradicional.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: Crear una función StartPlayback que acepte cualquier Playable
// y devuelva el resultado de llamar a Play()

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Leer el modelo del smartphone
	scanner.Scan()
	smartphoneModel := scanner.Text()
	
	// Leer la marca de audio del smartphone
	scanner.Scan()
	smartphoneBrand := scanner.Text()
	
	// Leer el modelo de la tablet
	scanner.Scan()
	tabletModel := scanner.Text()
	
	// Leer la marca de audio de la tablet
	scanner.Scan()
	tabletBrand := scanner.Text()
	
	// TODO: Crear un Smartphone con el AudioPlayer embebido
	
	// TODO: Crear una Tablet con el AudioPlayer embebido
	
	// TODO: Imprimir el modelo del smartphone, luego llamar a StartPlayback con el smartphone
	
	// TODO: Imprimir el modelo de la tablet, luego llamar a StartPlayback con la tablet
	
	_ = smartphoneModel
	_ = smartphoneBrand
	_ = tabletModel
	_ = tabletBrand
	fmt.Println("TODO: Complete the implementation")
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos