Menu
Coddy logo textTech

Incrustación de múltiples structs

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

Go permite incrustar múltiples estructuras dentro de una sola estructura, combinando comportamientos de diferentes fuentes. Esto es similar a la herencia múltiple en otros lenguajes, pero con el enfoque de composición de Go.

type Logger struct{}

func (l Logger) Log(msg string) string {
    return "LOG: " + msg
}

type Notifier struct{}

func (n Notifier) Notify(msg string) string {
    return "NOTIFY: " + msg
}

type Service struct {
    Name string
    Logger
    Notifier
}

La estructura Service ahora tiene acceso a los métodos de ambos tipos embebidos:

func main() {
    s := Service{Name: "OrderService"}
    fmt.Println(s.Log("started"))      // LOG: started
    fmt.Println(s.Notify("new order")) // NOTIFY: new order
}

Al incrustar múltiples estructuras, puede ocurrir un conflicto de nombres si dos tipos incrustados tienen métodos con el mismo nombre. Go no resuelve automáticamente esta ambigüedad:

type A struct{}
func (A) Greet() string { return "Hello from A" }

type B struct{}
func (B) Greet() string { return "Hello from B" }

type Combined struct {
    A
    B
}

func main() {
    c := Combined{}
    // c.Greet() - error de compilación: selector ambiguo
    fmt.Println(c.A.Greet())  // Hello from A
    fmt.Println(c.B.Greet())  // Hello from B
}

Cuando surgen conflictos, debes especificar explícitamente qué método del tipo incrustado deseas llamar utilizando el nombre del tipo como calificador.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de dispositivos para el hogar inteligente que combina múltiples capacidades a través de la incrustación de estructuras (struct embedding). Crearás un dispositivo que puede controlar tanto la iluminación como la reproducción de música mediante la incrustación de tipos de controladores independientes.

Organizarás tu código en tres archivos:

  • controllers.go: Crea dos estructuras de controlador independientes que proporcionen diferentes capacidades:
    • Un LightController con un campo Brightness (int) y un método SetLight(level int) string que devuelva "Light set to [level]%"
    • Un AudioController con un campo Volume (int) y un método SetVolume(level int) string que devuelva "Volume set to [level]%"
    Ambos controladores también deben tener un método Status() string; esto crea un conflicto de nombres que deberás gestionar. LightController.Status() debe devolver "Brightness: [Brightness]%" y AudioController.Status() debe devolver "Volume: [Volume]%".
  • device.go: Crea una estructura SmartDevice con un campo Name que incruste tanto LightController como AudioController. Añade un método llamado FullStatus() string que devuelva el nombre del dispositivo junto con los estados de ambos controladores, resolviendo la ambigüedad al llamar explícitamente al método Status() de cada tipo incrustado.
  • main.go: Lee la configuración del dispositivo desde la entrada, crea un SmartDevice, utiliza los métodos promovidos para establecer los niveles de luz y volumen, y luego muestra el estado completo.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre del dispositivo
  • Línea 2: Nivel de brillo inicial (entero)
  • Línea 3: Nivel de volumen inicial (entero)
  • Línea 4: Nuevo nivel de brillo a establecer (entero)
  • Línea 5: Nuevo nivel de volumen a establecer (entero)

Tu método FullStatus() debe devolver:

[Name] - [LightController.Status()], [AudioController.Status()]

Imprime los resultados de llamar a SetLight y SetVolume (que son promovidos desde los tipos incrustados), luego imprime el estado completo.

Por ejemplo, dados Living Room Hub, 50, 30, 75, y 60, tu salida debería ser:

Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%

Observa cómo SetLight y SetVolume son directamente accesibles en SmartDevice a través de la promoción de métodos, pero Status() requiere una calificación explícita porque ambos tipos incrustados tienen ese método.

Hoja de referencia

Go permite incrustar múltiples estructuras dentro de una sola estructura, combinando comportamientos de diferentes fuentes a través de la composición:

type Logger struct{}

func (l Logger) Log(msg string) string {
    return "LOG: " + msg
}

type Notifier struct{}

func (n Notifier) Notify(msg string) string {
    return "NOTIFY: " + msg
}

type Service struct {
    Name string
    Logger
    Notifier
}

La estructura Service tiene acceso a los métodos de ambos tipos incrustados:

s := Service{Name: "OrderService"}
fmt.Println(s.Log("started"))      // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order

Los conflictos de nombres ocurren cuando los tipos incrustados tienen métodos con el mismo nombre. Debes especificar explícitamente qué método del tipo incrustado llamar:

type A struct{}
func (A) Greet() string { return "Hello from A" }

type B struct{}
func (B) Greet() string { return "Hello from B" }

type Combined struct {
    A
    B
}

// c.Greet() - compile error: ambiguous selector
c := Combined{}
fmt.Println(c.A.Greet())  // Hello from A
fmt.Println(c.B.Greet())  // Hello from B

Pruébalo tú mismo

package main

import (
	"fmt"
)

func main() {
	// Leer entrada
	var name string
	fmt.Scanln(&name)

	var initialBrightness int
	fmt.Scanln(&initialBrightness)

	var initialVolume int
	fmt.Scanln(&initialVolume)

	var newBrightness int
	fmt.Scanln(&newBrightness)

	var newVolume int
	fmt.Scanln(&newVolume)

	// TODO: Crear un SmartDevice con el nombre y los valores iniciales dados

	// TODO: Usar el método SetLight promocionado e imprimir el resultado

	// TODO: Usar el método SetVolume promocionado e imprimir el resultado

	// TODO: Imprimir el estado completo usando el método FullStatus()
}
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