Ocultação de Informação em Go
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 50 de 107.
Ocultação de informações vai além de apenas tornar os campos não exportados. Trata-se de projetar seus tipos de modo que o código externo saiba apenas o que precisa saber — nada mais.
O objetivo é expor comportamento através de métodos enquanto mantém a estrutura interna completamente oculta. Considere esta abordagem:
type Counter struct {
value int
maxLimit int
}
func NewCounter(limit int) *Counter {
return &Counter{maxLimit: limit}
}
func (c *Counter) Increment() bool {
if c.value >= c.maxLimit {
return false
}
c.value++
return true
}
func (c *Counter) Value() int {
return c.value
}
O código externo não sabe que o Counter usa um int internamente. Poderia ser um float64, um slice ou algo totalmente diferente. A implementação pode mudar sem afetar nenhum código que use o Counter—desde que os métodos se comportem da mesma maneira.
Este princípio também se aplica a funções auxiliares. Mantenha a lógica interna não exportada:
// Exportado - parte da API pública
func (c *Counter) Reset() {
c.value = 0
}
// não exportado - auxiliar interno
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
Ao ocultar isAtLimit(), você tem a liberdade de alterá-lo ou removê-lo mais tarde. A ocultação de informações cria um limite claro entre o que seu pacote promete aos usuários externos e como ele funciona internamente.
Desafio
FácilVamos construir um sistema de carteira segura que demonstra a ocultação de informações. Você criará uma carteira digital onde o rastreamento do saldo interno e o histórico de transações ficam completamente ocultos do código externo — apenas o comportamento é exposto por meio de métodos.
Você organizará seu código em dois arquivos:
wallet.go: Crie uma structWalletque oculte todo o seu estado interno. A carteira deve rastrear o saldo atual e um limite de gastos internamente, mas o código externo nunca deve saber como eles são armazenados ou representados. Exponha o comportamento através destes métodos:NewWallet(limit float64) *Wallet- construtor que cria uma carteira com o limite de gastos fornecido e saldo zeroDeposit(amount float64) bool- adiciona dinheiro à carteira, retornatruese for bem-sucedido (o valor deve ser positivo)Spend(amount float64) bool- remove dinheiro se houver saldo suficiente E o valor não exceder o limite de gastos, retornatruese for bem-sucedidoBalance() float64- retorna o saldo atualStatus() string- retorna uma mensagem de status no formato:Balance: $[balance] (Limit: $[limit])
main.go: Leia a configuração da carteira e as transações da entrada, crie uma carteira, realize operações e exiba os resultados após cada ação.
As seguintes entradas serão fornecidas:
- Linha 1: Limite de gastos
- Linha 2: Valor do depósito
- Linha 3: Valor do primeiro gasto
- Linha 4: Valor do segundo gasto
Após criar a carteira, imprima seu status inicial. Em seguida, realize cada operação e imprima o status atualizado (se for bem-sucedido) ou Transaction failed (se a operação for rejeitada). Formate todos os valores monetários com duas casas decimais.
Por exemplo, dados 50, 100, 30 e 80, sua saída deve ser:
Balance: $0.00 (Limit: $50.00)
Balance: $100.00 (Limit: $50.00)
Balance: $70.00 (Limit: $50.00)
Transaction failedA última transação falha porque $80 excede o limite de gastos de $50 — mesmo que haja saldo suficiente. A carteira impõe suas regras internamente sem expor como toma essas decisões. O código externo simplesmente chama métodos e recebe resultados, sem nunca conhecer os detalhes da implementação interna.
Folha de consulta
Ocultação de informações significa projetar tipos de forma que o código externo saiba apenas o que precisa saber. Exponha o comportamento por meio de métodos, mantendo a estrutura interna completamente oculta.
Exemplo de um tipo com implementação oculta:
type Counter struct {
value int
maxLimit int
}
func NewCounter(limit int) *Counter {
return &Counter{maxLimit: limit}
}
func (c *Counter) Increment() bool {
if c.value >= c.maxLimit {
return false
}
c.value++
return true
}
func (c *Counter) Value() int {
return c.value
}
O código externo não conhece a representação interna (poderia ser int, float64 ou qualquer outra coisa). A implementação pode mudar sem afetar o código que a utiliza.
Mantenha as funções auxiliares não exportadas:
// Exportado - parte da API pública
func (c *Counter) Reset() {
c.value = 0
}
// não exportado - auxiliar interno
func (c *Counter) isAtLimit() bool {
return c.value >= c.maxLimit
}
A ocultação de informações cria um limite claro entre o que seu pacote promete externamente e como ele funciona internamente.
Experimente você mesmo
package main
import (
"fmt"
)
func main() {
// Ler as entradas
var limit float64
var depositAmount float64
var spend1 float64
var spend2 float64
fmt.Scanln(&limit)
fmt.Scanln(&depositAmount)
fmt.Scanln(&spend1)
fmt.Scanln(&spend2)
// TODO: Criar uma nova carteira com o limite de gastos fornecido
// TODO: Imprimir o status inicial
// TODO: Realizar o depósito e imprimir o resultado (status ou "Transaction failed")
// TODO: Realizar o primeiro gasto e imprimir o resultado
// TODO: Realizar o segundo gasto e imprimir o resultado
}
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