Menu
Coddy logo textTech

Shadowing de métodos incrustados

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

A veces necesitas que la estructura externa proporcione su propia implementación de un método que el tipo embebido ya tiene. Esto se llama shadowing, y te permite sobrescribir métodos promovidos con un comportamiento personalizado.

Cuando defines un método en la estructura externa con el mismo nombre que un método incrustado, el método externo tiene prioridad:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

Ahora, cuando llamas a Speak() en un Dog, se utiliza el método externo:

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    
    fmt.Println(d.Speak())        // ¡Guau! - método de Dog
    fmt.Println(d.Animal.Speak()) // Algún sonido - método de Animal
}

El método incrustado no ha desaparecido. Todavía puedes acceder a él explícitamente a través del nombre del tipo incrustado. Este patrón es útil cuando quieres personalizar el comportamiento manteniendo disponible la implementación original, tal vez para llamarla desde dentro de tu método de anulación:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

El sombreado le otorga control sobre qué comportamiento se expone mientras mantiene el acceso al original a través de una calificación explícita.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de notificaciones donde diferentes canales de notificación puedan personalizar cómo formatean y entregan los mensajes. Utilizarás el sombreado de métodos (method shadowing) para permitir que canales específicos anulen el comportamiento predeterminado, manteniendo la capacidad de acceder a la implementación original cuando sea necesario.

Organizarás tu código en tres archivos:

  • base.go: Crea una estructura BaseNotification con los campos Title y Priority (ambos strings). Dale un método Format() string que devuelva la notificación en un formato estándar: [Priority] Title.
  • channels.go: Crea dos tipos de canales de notificación que incrusten BaseNotification y sombreen el método Format con sus propias implementaciones:
    • EmailNotification con un campo Recipient—su Format() debe devolver Email to [Recipient]: [Priority] Title
    • SlackNotification con un campo Channel—su Format() debe llamar al método BaseNotification.Format() incrustado y anteponer #[Channel]: al resultado
  • main.go: Lee los detalles de la notificación de la entrada, crea ambos tipos de notificaciones y demuestra el sombreado imprimiendo:
    1. La salida formateada de EmailNotification (usando su método sombreado)
    2. El formato base de EmailNotification (llamando explícitamente al método incrustado)
    3. La salida formateada de SlackNotification (que utiliza internamente el método base)

Se proporcionarán las siguientes entradas:

  • Línea 1: Título de la notificación
  • Línea 2: Nivel de prioridad
  • Línea 3: Destinatario del correo electrónico
  • Línea 4: Nombre del canal de Slack

Por ejemplo, dados Server Alert, HIGH, admin@company.com, y ops-alerts, tu salida debería ser:

Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server Alert

Observa cómo EmailNotification reemplaza completamente el formateo base, mientras que SlackNotification lo mejora llamando al método original y añadiendo el contexto del canal. Ambos enfoques demuestran diferentes formas de utilizar el sombreado de métodos de manera efectiva.

Hoja de referencia

Cuando defines un método en una estructura externa con el mismo nombre que un método de un tipo incrustado, el método externo oculta (anula) al método incrustado:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

Al llamar al método en la estructura externa, el método externo tiene prioridad:

d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}

fmt.Println(d.Speak())        // Woof! - método de Dog
fmt.Println(d.Animal.Speak()) // Some sound - método de Animal

El método incrustado sigue siendo accesible mediante la calificación explícita utilizando el nombre del tipo incrustado. Puedes llamar a la implementación original desde dentro del método que lo anula:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

Pruébalo tú mismo

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Leer el título de la notificación
	scanner.Scan()
	title := scanner.Text()
	
	// Leer el nivel de prioridad
	scanner.Scan()
	priority := scanner.Text()
	
	// Leer el destinatario del correo electrónico
	scanner.Scan()
	recipient := scanner.Text()
	
	// Leer el nombre del canal de Slack
	scanner.Scan()
	channel := scanner.Text()
	
	// TODO: Crear una EmailNotification con la notificación base, title, priority y recipient
	
	// TODO: Crear una SlackNotification con la notificación base, title, priority y channel
	
	// TODO: Imprimir la salida formateada de EmailNotification (usando su método sombreado)
	
	// TODO: Imprimir el formato base de EmailNotification (llamando explícitamente al método embebido)
	
	// TODO: Imprimir la salida formateada de SlackNotification
	
	fmt.Println() // Marcador de posición - eliminar al implementar
}
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