Funções e Interfaces: O Modelo de Composição do Go
O Go compartilha comportamento através de funções, métodos e interfaces, em vez de herança de classes.
Busque em todas as páginas da documentação
O Go compartilha comportamento através de funções, métodos e interfaces, em vez de herança de classes.
Esse design mantém os tipos pequenos, as dependências explícitas e as APIs fáceis de testar sem "mágica" profunda de framework.
nil, funções variádicas e de ordem superior, convenções de retorno de erro.Linguagens orientadas a objetos tradicionais frequentemente modelam relacionamentos "é um" com herança: um HTTPServer é um Server, que é um Listener.
O Go omite deliberadamente esse mecanismo.
Em vez disso, um tipo contém dados (campos de struct) e métodos anexam comportamento por meio de receivers.
Quando dois tipos precisam de comportamento compartilhado, você extrai uma função ou define uma interface pequena que ambos satisfazem.
Interfaces em Go são conjuntos de assinaturas de métodos.
Um tipo concreto implementa uma interface automaticamente quando possui os métodos necessários - sem palavra-chave implements, sem etapa de geração de código.
Isso mantém os produtores desacoplados dos consumidores: o pacote de manipuladores HTTP define type Handler interface { ServeHTTP(...) }; sua struct apenas adiciona o método.
Embedding (campos de struct ou interface anônimos) é a outra alavanca de composição do Go.
Embedding promove métodos para o tipo externo, semelhante à delegação, mas não é herança - não há garantia de substituibilidade além do conjunto de métodos promovidos.
Uma analogia útil: tipos Go são peças de LEGO com pinos (métodos); interfaces são o formato do buraco que você precisa.
Qualquer peça com pinos correspondentes se encaixa, independentemente da cor ou estrutura interna.
O fluxo de controle em serviços Go geralmente se estrutura assim:
┌──────────────┐ depende de ┌──────────────┐
│ handler │ ────────────────► │ interface │
│ (concreto) │ │ (consumidor)│
└──────┬───────┘ └──────▲───────┘
│ chama │ satisfeito por
▼ │
┌──────────────┐ ┌──────┴───────┐
│ service │ │ repository │
│ struct │ │ mock / pg │
└──────────────┘ └──────────────┘
Funções são valores de primeira classe: você pode passá-las, retorná-las e armazená-las em variáveis.
Isso permite cadeias de middleware (func(http.Handler) http.Handler), políticas de repetição e duplas de teste sem boilerplate de interface quando uma única função é suficiente.
Métodos vinculam comportamento a um tipo e participam de conjuntos de métodos.
O conjunto difere para receivers de ponteiro vs. valor, o que afeta a satisfação da interface - um detalhe que prejudica equipes que misturam estilos de receiver em um tipo.
Interfaces são satisfeitas implicitamente em tempo de compilação.
O compilador verifica atribuições; o tempo de execução usa pesquisas itable para interfaces não vazias (par de palavra-chave de tipo + dados).
A interface vazia interface{} (ou any) contém qualquer valor, mas sacrifica a verificação estática - prefira interfaces tipadas nas fronteiras.
A frase idiomática "aceite interfaces, retorne structs" codifica a disciplina de composição: os chamadores dependem do menor comportamento de que precisam; os construtores retornam tipos concretos para que os chamadores não fiquem presos à sua forma de interface.
// Consumer define o contrato.
type Clock interface {
Now() time.Time
}
// Producer retorna tipo concreto; testes trocam via parâmetro de interface.
type RealClock struct{}
func (RealClock) Now() time.Time { return time.Now() }Grandes bases de código Go permanecem saudáveis quando as regras de composição são aplicadas na revisão, não deixadas à intuição.
| Abordagem | Força | Fraqueza | Melhor Encaixe |
|---|---|---|---|
| Interfaces de consumidor pequenas | Fakes fáceis, mocks estáveis | Muitas interfaces nomeadas | Bibliotecas, serviços de domínio |
Parâmetros de função (func(...)) | Cerimônia mínima | Mais difícil de compor APIs com múltiplos métodos | Middleware, hooks |
| Embedding de structs concretas | Reutilização rápida de padrões | Superfície de API promovida oculta | Wrappers de http.Server, decoradores |
| Genéricos com restrições | Algoritmos compartilhados | Pode obscurecer a intenção se usado em excesso | Coleções, parsers |
any nas fronteiras | Interoperabilidade com dados dinâmicos | Perde segurança em tempo de compilação | Apenas estágio de decodificação JSON |
Frameworks gRPC e HTTP (chi, gin, echo) compõem por meio de funções e middleware em formato de interface.
Seus pacotes de negócios ainda devem expor interfaces de domínio, não tipos de framework, para que a troca de roteadores não reescreva a lógica principal.
Kubernetes controller-runtime reconciliadores compõem por meio de interfaces (client.Client, reconciler.Reconciler), enquanto structs incorporam client.Client para delegação - um padrão de composição canônico em larga escala.
Testes substituem interfaces por fakes que registram chamadas.
Se uma interface crescer além de ~3 métodos, divida-a ou passe funções por grupo de métodos.
Versionamento: interfaces exportadas são compromissos de compatibilidade.
Adicionar métodos quebra implementadores; prefira novos nomes de interface (Reader + ReadCloser) ou interfaces não exportadas satisfeitas por structs exportadas.
== nil; isso quebra muitas verificações if x == nil.Evita hierarquias de classes base frágeis e substituições ocultas.
As equipes compõem o comportamento explicitamente com funções, interfaces pequenas e embedding, para que as mudanças permaneçam locais e testáveis.
Uma struct armazena dados.
Uma interface descreve o comportamento (assinaturas de métodos) e armazena um par de tipo e valor em tempo de execução quando atribuída.
Interfaces não carregam seus próprios campos.
Use métodos quando o comportamento estiver vinculado ao ciclo de vida de um tipo ou quando você precisar de um conjunto de métodos para interfaces.
Use funções no nível do pacote quando a operação for genérica e não precisar de estado do receiver.
Os parâmetros de função devem ser a menor interface de que os chamadores precisam.
Retorne structs concretas (ou tipos nomeados) para que os usuários não sejam forçados a depender de suas definições de interface.
Campos anônimos incorporados promovem métodos para o tipo externo.
Você reutiliza a implementação sem herança, mas os métodos promovidos exportados se tornam parte de sua API pública - incorpore deliberadamente.
Desacoplamento: os produtores não devem importar pacotes de consumidores apenas para declarar satisfação.
O compilador verifica os conjuntos de métodos nos locais de atribuição em vez disso.
Conjuntos de métodos incluem métodos de valor para T e *T, mas apenas métodos de ponteiro em *T.
A satisfação da interface depende de quais métodos são necessários e de qual tipo de receiver você atribui.
Parâmetros de tipo podem ser restritos por interfaces (interface { ~int | ~int64 } ou restrições padrão).
Use genéricos para algoritmos compartilhados entre tipos; use interfaces para polimorfismo em tempo de execução e limites de I/O.
Nem sempre - é apropriado em limites de decodificação.
Evite any em APIs de domínio onde uma interface tipada ou struct comunica a intenção aos leitores e ao compilador.
Manipuladores são funções ou tipos com ServeHTTP.
Componha preocupações transversais com middleware func(http.Handler) http.Handler em vez de classes de manipulador base.
Exportar interfaces amplas de pacotes produtores.
Isso bloqueia os consumidores com mocks grandes e torna a evolução da API dolorosa.
main conecta servidores concretos e passa context.Context através de interfaces.
A composição mantém a lógica de desligamento em um só lugar, em vez de cadeias de substituição dispersas.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão Green Tea GC, go fix modernizers - verifique o patch na compilação), chi (última - verifique na compilação), gin (última - verifique na compilação), echo (última - verifique na compilação), google.golang.org/grpc (última - verifique na compilação), sigs.k8s.io/controller-runtime (última - verifique na compilação), kubebuilder (última - verifique na compilação), tinygo (última - verifique os alvos de placa na compilação), wazero (última - verifique na compilação) e golangci-lint (última - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 18 de jul. de 2026