Uma dúvida que tenho visto com certa frequência é sobre as classes e herança em Go.
Sempre lembrando que GO não tem orientação a objetos na sua forma original de ser. Porém, quando o assunto é classe e herança, é possível alcançar algo similar utilizando structs e a técnica de embedding.
Tento esclarecido isso, vamos ver como podemos utilizar “herança” em Go imaginando um banco de dados com 4 tabelas.

Agora vamos escrever as structs que representam cada uma dessas tabelas, sem se preocupar com a relação entre elas ainda.
type Pessoa struct { Nome string Documento string DataNascimento time.Time } type PessoaJuridica struct { InscricaoEstadual string } type PessoaFisica struct { Sobrenome string RG string } type Endereco struct { Tipo string Endereco string Bairro string Cidade string Estado string }
Como a tabela pessoa
tem uma relação de 1:1 com as tabelas pessoa_juridica
e pessoa_fisica
, ou seja, uma relação de herança, vamos deixar para trabalhar nelas depois. Antes, vamos focar na relação de pessoa
e endereco
, pois essa relação será refletida na relação de herança.
A nossa tabela pessoa
tem uma relação de 1:N com a tabela endereco
, ou seja, para cada registro na tabela pessoa
, eu posso ter N registros na tabela endereco
.
E como N nada mais é do que um array, para trazer essa representação à nossa struct, vamos fazer a seguinte modificação.
type Pessoa struct { Nome string Documento string DataNascimento time.Time Enderecos []Endereco // campo adicionado }
Agora, para trazer a representação de 1:1 às nossas structs, vamos utilizar uma técnica chamada de embedding.
Essa técnica consiste em referenciar uma struct dentro de outra sem criar um atributo para isso.
No nosso caso, essa mudança deixará nossas structs PessoaJuridica
e PessoaFisica
da seguinte forma.
type PessoaJuridica struct { Pessoa // embedding InscricaoEstadual string } type PessoaFisica struct { Pessoa // embedding Sobrenome string RG string }
Utilizando essa técnica, conseguimos utilizar os atributos e métodos da struct Pessoa
de forma direta, sem ter que chamar um .Pessoa
antes.
// sem embedding fmt.Println(pj.Pessoa.Nome) // com embedding fmt.Println(pj.Nome)
Agora que temos todas as relações feitas, vamos adicionar três métodos.
O primeiro método vai retornar a idade da Pessoa
.
func (p *Pessoa) Idade() int { return time.Now().Year() - p.DataNascimento.Year() }
O segundo método vai retornar o nome completo da PessoaFisica
.
func (pf *PessoaFisica) NomeCompleto() string { return fmt.Sprintf("%s %s", pf.Nome, pf.Sobrenome) }
E o terceiro vai retornar o Endereco
formatado.
func (e *Endereco) Completo() string { return fmt.Sprintf("%s: %s, %s, %s - %s", e.Tipo, e.Endereco, e.Bairro, e.Cidade, e.Estado) }
Em um programinha bem simples, vamos iniciar uma struct do tipo PessoaFisica
e depois executar esses três métodos que criamos.
func main() { pf := PessoaFisica{ Pessoa{ Nome: "Tiago", Documento: "000.000.000-00", DataNascimento: time.Date(1990, 02, 22, 7, 0, 0, 0, time.Local), Enderecos: []Endereco{ { Tipo: "casa", Endereco: "Rua dos Gophers", Bairro: "Imperial", Cidade: "GoCity", Estado: "GOstate", }, }, }, "Temporin", "00.000.000-00", } fmt.Println("Nome:", pf.NomeCompleto()) fmt.Println("Idade:", pf.Idade()) for _, e := range pf.Enderecos { fmt.Println("Endereço:", e.Completo()) } }
Como podemos ver, utilizando a técnica de embedding para tratar relações de herança ajuda muito, pois todas as structs vão herdar seus atributos, métodos e relações.
Espero que tenha ajudado!
Se ficou alguma dúvida, deixa um comentário aí que eu respondo assim que possível.
Obrigado e até a próxima!