Visibilidade: exportado vs não exportado
1. Conceitos Fundamentais de Visibilidade em Go
Em Go, o sistema de visibilidade é elegantemente simples: a primeira letra do identificador determina tudo. Se começa com maiúscula, o identificador é exportado (público). Se começa com minúscula, é não exportado (privado ao pacote).
package matematica
// Exportado: pode ser usado fora do pacote
func Soma(a, b int) int {
return a + b
}
// Não exportado: visível apenas dentro do pacote
func subtrai(a, b int) int {
return a - b
}
Diferente de linguagens como Java ou C++ (que possuem public, private, protected), Go opera apenas com dois níveis: exportado (visível fora do pacote) e não exportado (visível dentro do pacote). Não existe "protected" – um identificador não exportado em um pacote pai não é acessível em subpacotes.
2. Exportação de Identificadores
Funções exportadas
Para criar APIs públicas, basta usar letra maiúscula:
package calculadora
func Somar(a, b float64) float64 {
return a + b
}
func Dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, erroDivisaoZero()
}
return a / b, nil
}
func erroDivisaoZero() error {
return fmt.Errorf("divisão por zero não permitida")
}
Tipos e structs exportados
Um struct exportado pode ter campos exportados e não exportados:
package usuario
type Usuario struct {
Nome string // Exportado
Email string // Exportado
senha string // Não exportado
}
func NovoUsuario(nome, email, senha string) *Usuario {
return &Usuario{
Nome: nome,
Email: email,
senha: hashSenha(senha),
}
}
Constantes e variáveis exportadas
package config
const Versao = "2.0.0" // Exportada
const versaoInterna = "2.0.0-beta" // Não exportada
var ConfiguracaoPadrao = Config{ // Exportada
Timeout: 30,
}
3. Identificadores Não Exportados e Encapsulamento
Métodos e funções internas
package validador
func validarCPF(cpf string) bool {
// Lógica interna de validação
return len(cpf) == 11
}
type Validador struct{}
func (v Validador) ValidarDocumento(doc string) bool {
return validarCPF(doc) // Usa função interna
}
Campos não exportados em structs
Protegem estado interno contra modificações diretas:
package conta
type ContaBancaria struct {
titular string
saldo float64 // Não exportado
}
func NovaConta(titular string) *ContaBancaria {
return &ContaBancaria{titular: titular, saldo: 0}
}
func (c *ContaBancaria) Depositar(valor float64) {
if valor > 0 {
c.saldo += valor
}
}
func (c *ContaBancaria) Saldo() float64 {
return c.saldo
}
4. Interfaces e Visibilidade
Interfaces exportadas com métodos não exportados
package armazenamento
type Armazenador interface {
Salvar(dados []byte) error
Carregar(id string) ([]byte, error)
validarChave(id string) bool // Não exportado, mas faz parte da interface
}
Tipos não exportados satisfazendo interfaces exportadas
package repositorio
type Repositorio interface {
Buscar(id int) string
}
type repositorioMySQL struct{}
func (r repositorioMySQL) Buscar(id int) string {
return "dados do MySQL"
}
Padrão de fábrica
package servico
type Servico interface {
Executar() string
}
type servicoImpl struct{}
func (s servicoImpl) Executar() string {
return "serviço executado"
}
func NovoServico() Servico {
return servicoImpl{} // Retorna tipo não exportado
}
5. Pacotes e Hierarquia de Visibilidade
Importação entre pacotes
// pacote principal
package main
import (
"meupacote/calculadora"
"meupacote/config"
)
func main() {
resultado := calculadora.Somar(2, 3) // OK
// calculadora.subtrai(5, 2) // ERRO: não exportado
fmt.Println(config.Versao) // OK
}
Subpacotes e falta de "protected"
Go não possui visibilidade hierárquica. Um identificador não exportado em pacote/a não é acessível em pacote/a/b:
// pacote/a/helper.go
var helperInterno = "secreto"
// pacote/a/b/helper.go
package b
import "meupacote/a"
func Teste() {
// a.helperInterno // ERRO: não exportado
}
6. Testes e Visibilidade
Testes no mesmo pacote (caixa branca)
Acessam identificadores não exportados:
package matematica
import "testing"
func TestSubtrai(t *testing.T) {
resultado := subtrai(10, 3) // Acessa função não exportada
if resultado != 7 {
t.Errorf("esperado 7, obtido %d", resultado)
}
}
Testes externos (caixa preta)
Usam _test no nome do pacote e só acessam exportados:
package matematica_test
import (
"testing"
"meupacote/matematica"
)
func TestSoma(t *testing.T) {
resultado := matematica.Soma(2, 3)
// matematica.subtrai(2, 3) // ERRO: não exportado
}
Exported testing helpers
package validador
// Exportado para testes
func ValidarCPFParaTeste(cpf string) bool {
return validarCPF(cpf)
}
7. Padrões e Boas Práticas
Exportar o mínimo necessário
package processador
// Exporte apenas o que for usado externamente
type Processador struct{}
func (p Processador) Processar(dados string) string {
return p.processarInterno(dados)
}
func (p Processador) processarInterno(dados string) string {
return "processado: " + dados
}
Renomeação em imports e visibilidade
import (
calc "meupacote/calculadora"
)
func main() {
calc.Somar(1, 2) // OK
}
Documentação de identificadores exportados
// Soma retorna a soma de dois inteiros.
// Exemplo de uso:
//
// resultado := Soma(3, 4)
// fmt.Println(resultado) // 7
func Soma(a, b int) int {
return a + b
}
Execute go doc para gerar documentação automaticamente.
8. Casos Especiais e Armadilhas Comuns
Erro comum: campo não exportado de struct exportado
package main
import "meupacote/usuario"
func main() {
u := usuario.NovoUsuario("João", "joao@email.com", "123")
fmt.Println(u.Nome) // OK
// fmt.Println(u.senha) // ERRO: campo não exportado
}
Exportação acidental
func processarDados() {
var DadosImportantes string // Maiúscula em variável local
_ = DadosImportantes
}
Reflexão (reflect) e visibilidade
package main
import (
"reflect"
"meupacote/conta"
)
func main() {
c := conta.NovaConta("João")
v := reflect.ValueOf(c)
campo := v.Elem().FieldByName("saldo")
// campo.IsValid() // true, mas não pode ser modificado
// campo.SetFloat(100) // PANIC: campo não exportado
}
Reflexão permite ler campos não exportados, mas não modificá-los. Evite contornar a visibilidade – isso quebra o encapsulamento e pode causar problemas de manutenção.
Resumo
O sistema de visibilidade em Go é simples, porém poderoso. A regra da primeira letra maiúscula/minúscula determina tudo, incentivando APIs limpas e encapsulamento eficaz. Lembre-se:
- Exporte o mínimo necessário para manter o contrato público enxuto
- Use tipos não exportados para esconder detalhes de implementação
- Prefira interfaces exportadas com implementações concretas não exportadas
- Respeite a visibilidade mesmo em testes – use caixa preta quando possível
Com essas práticas, seu código Go será mais modular, testável e fácil de manter.
Referências
- Documentação oficial: Exported identifiers — A especificação da linguagem Go define exatamente o que são identificadores exportados e como a visibilidade funciona.
- Effective Go: Package names — Guia oficial sobre boas práticas de nomenclatura e organização de pacotes em Go.
- Go by Example: Packages — Tutorial prático demonstrando exportação e importação de pacotes com exemplos interativos.
- The Go Blog: Package names — Artigo oficial do time Go sobre convenções de nomes de pacotes e visibilidade.
- Go 101: Visibility and Scope — Explicação detalhada sobre visibilidade, escopo e regras de exportação em Go.
- DigitalOcean: Understanding Package Visibility in Go — Tutorial completo sobre visibilidade de pacotes com exemplos práticos e casos de uso comuns.