Menu
Coddy logo textTech

Shadowing de Métodos Incorporados

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 39 de 107.

Às vezes, você precisa que a struct externa forneça sua própria implementação de um método que o tipo incorporado já possui. Isso é chamado de shadowing, e permite que você sobrescreva métodos promovidos com um comportamento personalizado.

Quando você define um método na struct externa com o mesmo nome de um método incorporado, o método externo tem precedência:

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!"
}

Agora, quando você chama Speak() em um Dog, o método externo é usado:

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    
    fmt.Println(d.Speak())        // Woof! - método do Dog
    fmt.Println(d.Animal.Speak()) // Some sound - método do Animal
}

O método incorporado não desapareceu. Você ainda pode acessá-lo explicitamente através do nome do tipo incorporado. Este padrão é útil quando você deseja personalizar o comportamento mantendo a implementação original disponível, talvez para chamá-la de dentro do seu método de sobreposição:

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

O sombreamento (shadowing) oferece controle sobre qual comportamento é exposto, mantendo o acesso ao original por meio de qualificação explícita.

challenge icon

Desafio

Fácil

Vamos construir um sistema de notificação onde diferentes canais de notificação podem personalizar como formatam e entregam mensagens. Você usará o sombreamento de métodos (method shadowing) para permitir que canais específicos substituam o comportamento padrão, mantendo a capacidade de acessar a implementação original quando necessário.

Você organizará seu código em três arquivos:

  • base.go: Crie uma struct BaseNotification com os campos Title e Priority (ambos strings). Adicione a ela um método Format() string que retorna a notificação em um formato padrão: "[Priority] Title".
  • channels.go: Crie dois tipos de canais de notificação que incorporam (embed) BaseNotification e sombreiam o método Format com suas próprias implementações:
    • EmailNotification com um campo Recipient—seu Format() deve retornar "Email to [Recipient]: [Priority] Title"
    • SlackNotification com um campo Channel—seu Format() deve chamar o método BaseNotification.Format() incorporado e prefixar "#[Channel]: " ao resultado
  • main.go: Leia os detalhes da notificação da entrada, crie ambos os tipos de notificações e demonstre o sombreamento imprimindo:
    1. A saída formatada da EmailNotification (usando seu método sombreado)
    2. O formato base da EmailNotification (chamando explicitamente o método incorporado)
    3. A saída formatada da SlackNotification (que usa internamente o método base)

As seguintes entradas serão fornecidas:

  • Linha 1: Título da notificação
  • Linha 2: Nível de prioridade
  • Linha 3: Destinatário do e-mail
  • Linha 4: Nome do canal do Slack

Por exemplo, dados "Server Alert", "HIGH", "admin@company.com" e "ops-alerts", sua saída deve ser:

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

Observe como a EmailNotification substitui completamente a formatação base, enquanto a SlackNotification a aprimora chamando o método original e adicionando o contexto do canal. Ambas as abordagens demonstram diferentes maneiras de usar o sombreamento de métodos de forma eficaz.

Folha de consulta

Quando você define um método em uma struct externa com o mesmo nome de um método de um tipo incorporado, o método externo sombreia (sobrescreve) o método incorporado:

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!"
}

Ao chamar o método na struct externa, o método externo tem precedência:

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

O método incorporado permanece acessível através de qualificação explícita usando o nome do tipo incorporado. Você pode chamar a implementação original de dentro do método de sobreposição:

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

Experimente você mesmo

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Ler o título da notificação
	scanner.Scan()
	title := scanner.Text()
	
	// Ler o nível de prioridade
	scanner.Scan()
	priority := scanner.Text()
	
	// Ler o destinatário do e-mail
	scanner.Scan()
	recipient := scanner.Text()
	
	// Ler o nome do canal do Slack
	scanner.Scan()
	channel := scanner.Text()
	
	// TODO: Criar uma EmailNotification com a notificação base, título, prioridade e destinatário
	
	// TODO: Criar uma SlackNotification com a notificação base, título, prioridade e canal
	
	// TODO: Imprimir a saída formatada da EmailNotification (usando seu método sombreado)
	
	// TODO: Imprimir o formato base da EmailNotification (chamando explicitamente o método incorporado)
	
	// TODO: Imprimir a saída formatada da SlackNotification
	
	fmt.Println() // Espaço reservado - remova ao implementar
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos