Menu
Coddy logo textTech

Duck Typing em Go

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

O termo duck typing vem do ditado: "Se ele anda como um pato e grasna como um pato, então é um pato." Em Go, isso significa que um tipo não precisa declarar explicitamente que implementa uma interface — ele só precisa ter os métodos corretos.

Considere esta interface e dois tipos completamente não relacionados:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Nem Duck nem Robot mencionam Quacker em qualquer lugar de suas definições. Ainda assim, ambos satisfazem a interface simplesmente porque possuem um método Quack() com a assinatura correta:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Esta satisfação implícita é poderosa porque permite que você defina interfaces após os tipos já existirem. Você pode criar uma interface que corresponda a tipos de pacotes externos sem modificar seu código-fonte. Os tipos não precisam saber sobre sua interface — eles apenas precisam se comportar corretamente.

Duck typing em Go oferece a flexibilidade de linguagens dinâmicas, mantendo a segurança de tipos em tempo de compilação. O compilador verifica se os tipos realmente possuem os métodos necessários antes de permitir que sejam usados como valores de interface.

challenge icon

Desafio

Fácil

Vamos construir um sistema de mensagens que demonstra o duck typing em ação. Você criará tipos completamente não relacionados que podem enviar mensagens através de uma interface comum — sem que nenhum deles declare explicitamente que a implementa.

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

  • messengers.go: Crie três structs não relacionadas que compartilham o mesmo comportamento:
    • Phone com um campo Number — seu método SendMessage(text string) string retorna "SMS to [Number]: [text]"
    • Computer com um campo Email — seu método SendMessage(text string) string retorna "Email to [Email]: [text]"
    • Pigeon com um campo Name — seu método SendMessage(text string) string retorna "[Name] carries: [text]"
    Observe que nenhum desses tipos menciona qualquer interface — eles apenas possuem a mesma assinatura de método.
  • sender.go: Defina uma interface Messenger que requer um método SendMessage(text string) string. Crie uma função chamada Broadcast que recebe um slice de Messenger e uma string de mensagem, e então retorna um slice de strings contendo o resultado da chamada de SendMessage em cada mensageiro.
  • main.go: Leia os detalhes da entrada, crie um de cada tipo de mensageiro, colete-os em um slice de Messenger e use Broadcast para enviar uma mensagem através de todos eles. Imprima cada resultado em sua própria linha.

As seguintes entradas serão fornecidas:

  • Linha 1: Número de telefone
  • Linha 2: Endereço de e-mail
  • Linha 3: Nome do pombo
  • Linha 4: Mensagem para transmitir

Por exemplo, dados "555-1234", "alice@mail.com", "Winston" e "Hello World", sua saída deve ser:

SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello World

O ponto principal aqui é que Phone, Computer e Pigeon não têm nada em comum — eles não compartilham um tipo base nem implementam explicitamente Messenger. No entanto, como todos eles "fazem quack" da mesma maneira (possuem o método correspondente), o Go permite que todos sejam usados como valores de Messenger. Isso é o duck typing em funcionamento.

Folha de consulta

Em Go, o duck typing significa que um tipo não precisa declarar explicitamente que implementa uma interface — ele só precisa ter os métodos necessários com assinaturas correspondentes.

Um tipo satisfaz implicitamente uma interface se possuir todos os métodos que a interface exige:

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Tanto Duck e Robot satisfazem a interface Quacker sem mencioná-la em suas definições:

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Essa satisfação implícita permite que você defina interfaces após os tipos já existirem, e até mesmo crie interfaces que correspondam a tipos de pacotes externos sem modificar seu código-fonte. O compilador verifica em tempo de compilação se os tipos possuem os métodos necessários, proporcionando segurança de tipos enquanto mantém a flexibilidade.

Experimente você mesmo

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Ler número de telefone
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Ler endereço de e-mail
	scanner.Scan()
	email := scanner.Text()
	
	// Ler nome do pombo
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Ler mensagem para transmitir
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Criar um de cada tipo de mensageiro (Telefone, Computador, Pombo)
	
	// TODO: Coletar todos os mensageiros em um slice de Messenger
	
	// TODO: Usar Broadcast para enviar a mensagem através de todos os mensageiros
	
	// TODO: Imprimir cada resultado em sua própria linha
	
	// Espaço reservado para usar variáveis (remover ao implementar)
	_ = phoneNumber
	_ = email
	_ = pigeonName
	_ = message
}
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