Encapsulamento em Nível de Pacote
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 48 de 107.
Enquanto a lição anterior abordou a visibilidade ao nível de campo, o encapsulamento do Go na verdade opera no nível de pacote. Tudo dentro do mesmo pacote — independentemente do arquivo em que esteja — pode acessar campos e funções não exportados.
Considere um pacote com múltiplos arquivos:
// account/account.go
package account
type Account struct {
balance int // não exportado
}
func (a *Account) Deposit(amount int) {
a.balance += amount
}
// account/helper.go
package account
func ResetAccount(a *Account) {
a.balance = 0 // funciona - mesmo pacote
}
Ambos os arquivos pertencem ao pacote account, então o helper.go pode acessar diretamente o campo balance não exportado. Isso é intencional — permite que você divida a lógica do pacote entre arquivos enquanto mantém o acesso interno.
No entanto, o código fora do pacote não pode acessar membros não exportados:
// main.go
package main
import "account"
func main() {
acc := &account.Account{}
acc.Deposit(100) // funciona - método exportado
// acc.balance = 50 // erro - campo não exportado
// account.ResetAccount // funcionaria se ResetAccount fosse exportado
}
Este limite de nível de pacote é o principal mecanismo de encapsulamento do Go. Você projeta pacotes como unidades coesas onde o código interno colabora livremente, enquanto o código externo interage apenas por meio da API exportada. Pense em cada pacote como um módulo independente com uma interface pública clara.
Desafio
FácilVamos construir um sistema de gerenciamento de inventário que demonstra como múltiplos arquivos dentro do mesmo pacote podem colaborar acessando campos não exportados diretamente, enquanto mantêm esses dados ocultos de códigos externos.
Você criará três arquivos que trabalham juntos no pacote main:
product.go: Defina uma structProductcom um campo exportadoNamee um campo não exportadoquantity(int). Adicione um método exportadoInfo() stringque retorna o formato:[Name]: [quantity] in stockinventory.go: Crie funções auxiliares que trabalham com produtos acessando diretamente o campo não exportadoquantity(possível porque estão no mesmo pacote). Implemente:Restock(p *Product, amount int)- adiciona a quantidade ao estoque do produtoSell(p *Product, amount int) bool- subtrai a quantidade do estoque se houver saldo suficiente, retornatruese for bem-sucedido oufalsese não houver estoque suficienteNewProduct(name string, initialQty int) *Product- construtor que cria um produto com o nome e a quantidade inicial fornecidos
main.go: Leia os detalhes do produto da entrada, crie um produto usando o construtor, realize operações de inventário e exiba os resultados. Após cada operação, imprima as informações do produto para mostrar o estado atualizado.
As seguintes entradas serão fornecidas:
- Linha 1: Nome do produto
- Linha 2: Quantidade inicial (inteiro)
- Linha 3: Quantidade de reposição (inteiro)
- Linha 4: Quantidade de venda (inteiro)
Após criar o produto, imprima suas informações iniciais. Em seguida, reponha o estoque e imprima as informações novamente. Por fim, tente vender e imprima as informações atualizadas (se for bem-sucedido) ou Sale failed: insufficient stock (se não houver quantidade suficiente).
Por exemplo, dados Laptop, 10, 5, e 8, sua saída deve ser:
Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stockE dados Phone, 3, 2, e 10, sua saída deve ser:
Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stockO ponto principal aqui é que o inventory.go pode ler e modificar diretamente o campo não exportado quantity porque ele está no mesmo pacote que o product.go. Isso é o encapsulamento em nível de pacote em ação — a colaboração interna acontece livremente enquanto os dados permanecem protegidos de pacotes externos.
Folha de consulta
O encapsulamento do Go opera no nível de pacote, não no nível de arquivo. Todos os arquivos dentro do mesmo pacote podem acessar campos e funções não exportados, independentemente de em qual arquivo eles foram definidos.
Acesso em Nível de Pacote
Membros não exportados são acessíveis em todos os arquivos do mesmo pacote:
// account/account.go
package account
type Account struct {
balance int // não exportado
}
func (a *Account) Deposit(amount int) {
a.balance += amount
}
// account/helper.go
package account
func ResetAccount(a *Account) {
a.balance = 0 // funciona - mesmo pacote
}
Restrições de Pacotes Externos
Código fora do pacote não pode acessar membros não exportados:
// main.go
package main
import "account"
func main() {
acc := &account.Account{}
acc.Deposit(100) // funciona - método exportado
// acc.balance = 50 // erro - campo não exportado
}
Este design permite que você divida a lógica do pacote em vários arquivos, mantendo o acesso interno, criando unidades coesas com interfaces públicas claras.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler o nome do produto
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Ler a quantidade inicial
initialQtyStr, _ := reader.ReadString('\n')
initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))
// Ler a quantidade de reposição
restockStr, _ := reader.ReadString('\n')
restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))
// Ler a quantidade de venda
sellStr, _ := reader.ReadString('\n')
sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))
// TODO: Criar um novo produto usando o construtor `NewProduct`
// TODO: Imprimir as informações iniciais do produto
// TODO: Repor o produto e imprimir as informações
// TODO: Tentar vender e imprimir o resultado
// Se a venda for bem-sucedida, imprimir as informações atualizadas
// Se a venda falhar, imprimir "Sale failed: insufficient stock"
fmt.Println() // Substituir pela saída real
}
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