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 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 nenhum lugar de suas definições. Ainda assim, ambos satisfazem a interface simplesmente porque têm um método Quack() com a assinatura correta:
func MakeItQuack(q Quacker) {
fmt.Println(q.Quack())
}
func main() {
MakeItQuack(Duck{}) // Quá!
MakeItQuack(Robot{}) // Bip-quá!
}
Essa satisfação implícita é poderosa porque permite definir interfaces depois que os tipos já existem. Você pode criar uma interface que corresponda a tipos de pacotes externos sem modificar o código-fonte deles. Os tipos não precisam conhecer sua interface. Eles só precisam se comportar corretamente.
A tipagem pato em Go oferece a flexibilidade das linguagens dinâmicas, mantendo a segurança de tipos em tempo de compilação. O compilador verifica se os tipos realmente têm os métodos necessários antes de permitir que sejam usados como valores de interface.
Desafio
FácilVamos construir um sistema de mensagens que demonstra a tipagem pato em ação. Você criará tipos completamente não relacionados que podem enviar mensagens por meio 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, por acaso, compartilham o mesmo comportamento:Phonecom um campoNumber. Seu métodoSendMessage(text string) stringretornaSMS to [Number]: [text]Computercom um campoEmail. Seu métodoSendMessage(text string) stringretornaEmail to [Email]: [text]Pigeoncom um campoName. Seu métodoSendMessage(text string) stringretorna[Name] carries: [text]
sender.go: Defina uma interfaceMessengerque exige um métodoSendMessage(text string) string. Crie uma função chamadaBroadcastque receba um slice deMessengere uma string de mensagem, depois retorne um slice de strings contendo o resultado da chamada deSendMessageem cada mensageiro.main.go: Leia os dados da entrada, crie um mensageiro de cada tipo, reúna-os em um slice deMessengere useBroadcastpara enviar uma mensagem por meio 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 WorldA principal ideia 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 "grasnam" da mesma forma (possuem o método correspondente), Go permite que todos sejam usados como valores de Messenger. Isso é a tipagem pato em ação.
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()
// Read email address
scanner.Scan()
email := scanner.Text()
// Ler nome do pombo
scanner.Scan()
pigeonName := scanner.Text()
// Ler mensagem para transmitir
scanner.Scan()
message := scanner.Text()
// TODO: Crie um de cada tipo de mensageiro (Phone, Computer, Pigeon)
// TODO: Colete todos os mensageiros em um slice de Messenger
// TODO: Use Broadcast para enviar a mensagem por todos os mensageiros
// TODO: Imprima cada resultado em sua própria linha
// Placeholder para usar as variáveis (remover ao implementar)
_ = phoneNumber
_ = email
_ = pigeonName
_ = message
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online