Fundamentos de Incorporação de Structs
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 35 de 107.
Incorporação de struct é o mecanismo do Go para incluir um tipo struct dentro de outro. Em vez de usar um nome de campo, você simplesmente especifica o tipo, e o Go promove os campos da struct incorporada para a struct externa.
Aqui está a sintaxe básica:
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // incorporado - sem nome de campo
}
Quando você incorpora Address em Person, você pode acessar os campos incorporados diretamente na struct externa:
func main() {
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
fmt.Println(p.City) // London - acesso direto
fmt.Println(p.Country) // UK - acesso direto
fmt.Println(p.Address.City) // também funciona
}
Observe como p.City funciona sem escrever explicitamente p.Address.City. O Go promove automaticamente os campos da struct incorporada, tornando-os acessíveis como se pertencessem à struct externa. Você ainda pode acessá-los através do nome do tipo incorporado quando necessário.
Isso difere de structs aninhadas regulares, onde você deve sempre usar o nome do campo para acessar os campos internos. A incorporação cria uma API mais plana e conveniente, mantendo o agrupamento lógico de dados relacionados.
Desafio
FácilVamos construir um sistema de registro de veículos que utiliza incorporação de structs (struct embedding) para criar um modelo de dados limpo e organizado. Você separará as informações de contato dos detalhes do veículo, permitindo o acesso direto a todos os campos por meio da incorporação.
Você organizará seu código em dois arquivos:
vehicle.go: Crie duas structs que trabalham juntas por meio de incorporação:- Uma struct
ContactInfocom os camposOwnerNameePhone(ambos strings) - Uma struct
Vehicleque incorporaContactInfo(sem um nome de campo) e adiciona seus próprios camposMake,ModeleYear
SummaryemVehicleque retorna uma string formatada contendo todas as informações do veículo e de contato.- Uma struct
main.go: Leia os detalhes do veículo e do proprietário da entrada, crie uma instância deVehiclecomContactInfoincorporado e demonstre o acesso aos campos incorporados tanto diretamente quanto através do nome do tipo incorporado. Imprima o resumo do veículo ao final.
As seguintes entradas serão fornecidas:
- Linha 1: Nome do proprietário
- Linha 2: Número de telefone
- Linha 3: Marca do veículo
- Linha 4: Modelo do veículo
- Linha 5: Ano (inteiro)
Seu método Summary deve retornar uma string neste formato:
[Year] [Make] [Model] - Owner: [OwnerName], Phone: [Phone]No seu arquivo principal, demonstre a promoção de campos (field promotion) imprimindo:
- O nome do proprietário acessado diretamente no Vehicle (ex:
v.OwnerName) - O telefone acessado através do tipo incorporado (ex:
v.ContactInfo.Phone) - O resumo completo do seu método
Summary
Por exemplo, dados Alice Smith, 555-1234, Toyota, Camry e 2023, sua saída deve ser:
Alice Smith
555-1234
2023 Toyota Camry - Owner: Alice Smith, Phone: 555-1234Observe como ambos os padrões de acesso funcionam — acesso direto por meio da promoção de campos e acesso explícito por meio do nome do tipo incorporado — oferecendo flexibilidade na forma como você trabalha com dados incorporados.
Folha de consulta
O encapsulamento de structs (struct embedding) permite incluir um tipo struct dentro de outro sem especificar um nome de campo. Os campos da struct incorporada são promovidos para a struct externa, tornando-os diretamente acessíveis.
Sintaxe Básica
type Address struct {
City string
Country string
}
type Person struct {
Name string
Address // embedded - no field name
}
Acessando Campos Incorporados
Você pode acessar campos incorporados de duas maneiras:
p := Person{
Name: "Alice",
Address: Address{
City: "London",
Country: "UK",
},
}
// Acesso direto (promoção de campo)
fmt.Println(p.City) // London
// Através do nome do tipo incorporado
fmt.Println(p.Address.City) // London
O Go promove automaticamente os campos da struct incorporada, permitindo o acesso direto como se eles pertencessem à struct externa. Você ainda pode acessá-los através do nome do tipo incorporado quando necessário.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lê o nome do proprietário
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Lê o número de telefone
phone, _ := reader.ReadString('\n')
phone = strings.TrimSpace(phone)
// Lê a marca do veículo
make, _ := reader.ReadString('\n')
make = strings.TrimSpace(make)
// Lê o modelo do veículo
model, _ := reader.ReadString('\n')
model = strings.TrimSpace(model)
// Lê o ano
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
// TODO: Criar uma instância de Vehicle com ContactInfo incorporado (embedded)
// Use os valores lidos da entrada acima
// TODO: Imprimir o nome do proprietário acessado diretamente no Vehicle (promoção de campo)
// Exemplo: fmt.Println(v.OwnerName)
// TODO: Imprimir o telefone acessado através do nome do tipo incorporado
// Exemplo: fmt.Println(v.ContactInfo.Phone)
// TODO: Imprimir o resumo completo usando o método Summary
// Exemplo: fmt.Println(v.Summary())
// Remova estas linhas assim que implementar a solução
_ = ownerName
_ = phone
_ = make
_ = model
_ = year
}
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 ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool