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 incluir varios structs dentro de un solo struct, combinando comportamientos de distintas 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 integrar varias estructuras, puede producirse un conflicto de nombres si dos tipos integrados 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 de A
fmt.Println(c.B.Greet()) // Hola de B
}
Cuando surjan conflictos, debes especificar explícitamente qué método del tipo integrado quieres llamar usando el nombre del tipo como calificador.
Desafío
FácilVamos a crear un sistema de dispositivos para el hogar inteligente que combine varias capacidades mediante la incrustación de estructuras. Crearás un dispositivo que pueda controlar la iluminación y reproducir música al incrustar tipos de controlador independientes.
Organizarás el 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 incruste tantoLightControllercomoAudioController. Añade un método llamadoFullStatus() stringque devuelva el nombre del dispositivo junto con los estados de ambos controladores y resuelva la ambigüedad llamando explícitamente al métodoStatus()de cada tipo incrustado.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 inicial de brillo (entero)
- Línea 3: Nivel inicial de volumen (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 incrustados) y, después, imprime el estado completo.
Por ejemplo, dadas las entradas Lounge, 50, 30, 75 y 60, la salida debe ser:
Light set to 75%
Volume set to 60%
Lounge - Brightness: 75%, Volume: 60%Observa que se puede acceder directamente a SetLight y SetVolume desde SmartDevice mediante la promoción de métodos, pero Status() requiere una calificación explícita porque ambos tipos incrustados tienen ese método.
Pruébalo tú mismo
package main
import (
"fmt"
)
func main() {
// Lee la 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 y los valores iniciales proporcionados
// 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