Menu
Coddy logo textTech

Promoção de Métodos

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

Assim como a incorporação de structs promove campos para a struct externa, o Go também promove métodos de tipos incorporados. Isso significa que você pode chamar métodos definidos na struct incorporada diretamente na struct externa.

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // incorporado
}

Como a Car incorpora a Engine, o método Start() é promovido automaticamente:

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // também funciona
}

Essa promoção tem uma consequência importante para interfaces. Se o tipo incorporado satisfaz uma interface, o tipo externo automaticamente a satisfaz também:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisfaz Starter através de Engine
}

O tipo Car nunca implementa explicitamente Starter, mas ele satisfaz a interface porque seu Engine incorporado possui o método necessário. É assim que o Go alcança o reuso de comportamento sem a herança tradicional.

challenge icon

Desafio

Fácil

Vamos construir um sistema de player de música que demonstra como os métodos de tipos incorporados (embedded) são promovidos para o tipo externo e como isso permite a satisfação de interfaces por meio da composição.

Você organizará seu código em dois arquivos:

  • audio.go: Crie os blocos de construção para o seu player de música:
    • Uma struct AudioPlayer com um campo Brand (string) e um método Play() string que retorna [Brand] playing audio
    • Uma interface Playable que exige um método Play() string
    • Uma struct Smartphone com um campo Model que incorpora AudioPlayer
    • Uma struct Tablet com um campo Model que incorpora AudioPlayer
  • main.go: Crie uma função chamada StartPlayback que aceita qualquer Playable e retorna o resultado da chamada de Play(). Leia as informações do dispositivo a partir da entrada, crie tanto um Smartphone quanto um Tablet, e demonstre que ambos satisfazem a interface Playable por meio de seu AudioPlayer incorporado — mesmo que nenhum dos tipos implemente explicitamente o método Play() por si só.

As seguintes entradas serão fornecidas:

  • Linha 1: Nome do modelo do Smartphone
  • Linha 2: Marca de áudio do Smartphone
  • Linha 3: Nome do modelo do Tablet
  • Linha 4: Marca de áudio do Tablet

Para cada dispositivo, imprima seu modelo seguido pelo resultado de passá-lo para StartPlayback.

Por exemplo, dados iPhone 15, Apple Audio, iPad Pro e Beats, sua saída deve ser:

iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audio

Observe como tanto Smartphone quanto Tablet podem ser passados para StartPlayback porque o método Play() de AudioPlayer é automaticamente promovido para cada tipo externo, satisfazendo a interface Playable sem qualquer código adicional.

Folha de consulta

Quando uma struct incorpora outro tipo, os métodos do tipo incorporado são automaticamente promovidos para a struct externa:

type Engine struct {
    Horsepower int
}

func (e Engine) Start() string {
    return "Engine started"
}

type Car struct {
    Model string
    Engine  // incorporado
}

func main() {
    c := Car{
        Model:  "Sedan",
        Engine: Engine{Horsepower: 200},
    }
    
    fmt.Println(c.Start())         // Engine started
    fmt.Println(c.Engine.Start())  // também funciona
}

Se um tipo incorporado satisfaz uma interface, o tipo externo automaticamente a satisfaz também:

type Starter interface {
    Start() string
}

func Ignite(s Starter) {
    fmt.Println(s.Start())
}

func main() {
    c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
    Ignite(c)  // Car satisfaz Starter através de Engine
}

Isso permite o reuso de comportamento através de composição sem a herança tradicional.

Experimente você mesmo

package main

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

// TODO: Crie uma função StartPlayback que aceite qualquer Playable
// e retorne o resultado da chamada de Play()

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Ler o modelo do smartphone
	scanner.Scan()
	smartphoneModel := scanner.Text()
	
	// Ler a marca de áudio do smartphone
	scanner.Scan()
	smartphoneBrand := scanner.Text()
	
	// Ler o modelo do tablet
	scanner.Scan()
	tabletModel := scanner.Text()
	
	// Ler a marca de áudio do tablet
	scanner.Scan()
	tabletBrand := scanner.Text()
	
	// TODO: Crie um Smartphone com o AudioPlayer incorporado
	
	// TODO: Crie um Tablet com o AudioPlayer incorporado
	
	// TODO: Imprima o modelo do smartphone e, em seguida, chame StartPlayback com o smartphone
	
	// TODO: Imprima o modelo do tablet e, em seguida, chame StartPlayback com o tablet
	
	_ = smartphoneModel
	_ = smartphoneBrand
	_ = tabletModel
	_ = tabletBrand
	fmt.Println("TODO: Complete the implementation")
}
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