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 te permite incrustar varias 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 integrados:
func main() {
s := Service{Name: "OrderService"}
fmt.Println(s.Log("started")) // LOG: iniciado
fmt.Println(s.Notify("new order")) // NOTIFY: nuevo pedido
}
Al incorporar varias estructuras, puede producirse un conflicto de nombres si dos tipos incorporados 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()) // Hola desde A
fmt.Println(c.B.Greet()) // Hola desde B
}
Cuando surjan conflictos, debes especificar explícitamente qué método del tipo incrustado quieres llamar usando el nombre del tipo como calificador.
Desafío
FácilConstruyamos un sistema de dispositivos domésticos inteligentes que combine varias capacidades mediante la inclusión de estructuras. Crearás un dispositivo que pueda controlar la iluminación y reproducir música mediante la inclusión de tipos de controlador independientes.
Organizarás tu código en tres archivos:
controllers.go: Crea dos estructuras de controlador independientes que proporcionen distintas capacidades:- Un
LightControllercon un campoBrightness(int) y un métodoSetLight(level int) stringque devuelvaLight set to [level]% - Un
AudioControllercon un campoVolume(int) y un métodoSetVolume(level int) stringque devuelvaVolume set to [level]%
Status() string. Esto crea un conflicto de nombres que tendrás que resolver.LightController.Status()debe devolverBrightness: [Brightness]%yAudioController.Status()debe devolverVolume: [Volume]%.- Un
device.go: Crea una estructuraSmartDevicecon un campoNameque incluya tantoLightControllercomoAudioController. Añade un método llamadoFullStatus() stringque devuelva el nombre del dispositivo junto con los estados de ambos controladores, resolviendo la ambigüedad mediante una llamada explícita al métodoStatus()de cada tipo incluido.main.go: Lee la configuración del dispositivo desde la entrada, crea unSmartDevice, usa los métodos promovidos para establecer los niveles de iluminación y volumen, y después 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 que se establecerá (entero)
- Línea 5: Nuevo nivel de volumen que se establecerá (entero)
Tu método FullStatus() debe devolver:
[Name] - [LightController.Status()], [AudioController.Status()]Imprime los resultados de llamar a SetLight y SetVolume (que se promueven desde los tipos incluidos) y después imprime el estado completo.
Por ejemplo, dadas las entradas 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 están directamente disponibles en SmartDevice mediante la promoción de métodos, pero Status() requiere una calificación explícita porque ambos tipos incluidos tienen ese método.
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: Crea un SmartDevice con el nombre dado y los valores iniciales
// TODO: Usa el método promovido SetLight e imprime el resultado
// TODO: Usa el método promovido SetVolume e imprime el resultado
// TODO: Imprime el estado completo usando el método FullStatus()
}
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
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker PoolPractica por tu cuenta: Compilador de Go online