Ocultação de Informações 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 simplesmente tornar os campos não exportados. Trata-se de projetar seus tipos para que o código externo saiba apenas o que precisa saber. Nada mais.
O objetivo é expor o comportamento por meio de métodos, mantendo 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
}
Código externo não sabe que Counter usa um int internamente. Ele poderia ser um float64, uma fatia ou algo completamente diferente. A implementação pode mudar sem afetar nenhum código que use Counter, desde que os métodos se comportem da mesma maneira.
Esse princípio também se aplica às 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ê fica livre para alterá-la ou removê-la posteriormente. 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 criar um sistema de carteira segura que demonstre o encapsulamento de informações. Você criará uma carteira digital em que o controle do saldo interno e o histórico de transações ficam completamente ocultos do código externo: somente 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 controlar internamente o saldo atual e um limite de gastos, mas o código externo nunca deve saber como eles são armazenados ou representados. Exponha o comportamento por meio 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 e 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, retornandotruese 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, execute as 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: Primeiro valor a gastar
- Linha 4: Segundo valor a gastar
Depois de criar a carteira, imprima seu status inicial. Em seguida, execute cada operação e imprima o status atualizado (se for bem-sucedida) ou Transaction failed (se a operação for rejeitada). Formate todos os valores em dólares com duas casas decimais.
Por exemplo, dadas as entradas 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, embora haja saldo suficiente. A carteira aplica 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 internos de implementação.
Experimente você mesmo
package main
import (
"fmt"
)
func main() {
// Ler 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: Perform deposit and print result (status or "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 ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online