io.Reader e io.Writer
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 73 de 107.
A biblioteca padrão do Go demonstra o design baseado em interfaces por meio de duas interfaces fundamentais: io.Reader e io.Writer. Essas interfaces simples dão suporte a tudo, desde operações com arquivos até comunicação de rede.
A interface io.Reader exige apenas um método:
type Reader interface {
Read(p []byte) (n int, err error)
}Ele lê até len(p) bytes na fatia e retorna quantos bytes foram lidos. Da mesma forma, io.Writer define:
type Writer interface {
Write(p []byte) (n int, err error)
}Qualquer tipo que implemente esses métodos funciona com todo o ecossistema de E/S.
Aqui está um tipo personalizado que implementa io.Reader:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}Agora este leitor personalizado funciona com qualquer função que espera um io.Reader:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAAEsse design permite escrever funções que aceitam io.Reader ou io.Writer, fazendo com que funcionem com arquivos, conexões de rede, buffers ou qualquer implementação personalizada.
Desafio
FácilVamos criar um processador personalizado de fluxo de texto que implemente as interfaces io.Reader e io.Writer! Você criará tipos capazes de transformar o texto à medida que ele passa por eles, demonstrando como essas interfaces fundamentais permitem uma composição poderosa de E/S.
Você organizará seu código em dois arquivos:
streams.go: defina seus tipos personalizados de leitor e gravador.Crie uma struct
CountingReaderque envolva uma string e acompanhe quantos bytes foram lidos. Ela deve ter campos para a string de origem e um rastreador de posição. Implemente o métodoRead(p []byte) (int, error), que lê bytes da origem para a fatia fornecida, atualiza a posição e retornaio.EOFquando a origem se esgotar.Crie uma struct
UppercaseWriterque colete os dados gravados e os converta para maiúsculas. Ela deve armazenar internamente o resultado acumulado. Implemente o métodoWrite(p []byte) (int, error), que converte os bytes recebidos para maiúsculas e os armazena. Adicione um métodoResult() stringpara recuperar o texto acumulado em maiúsculas.Crie as funções construtoras
NewCountingReader(source string) *CountingReadereNewUppercaseWriter() *UppercaseWriterpara inicializar seus tipos corretamente.main.go: demonstre seus tipos personalizados de E/S funcionando com a biblioteca padrão.Leia uma entrada de string e, em seguida, use seu
CountingReadercomio.ReadAllpara ler todos os dados. Passe esses dados pelo seuUppercaseWriterusando o métodoWrite.Imprima os resultados neste formato:
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
A seguinte entrada será fornecida:
- Uma única linha de texto
Por exemplo, dada a entrada:
Hello WorldSua saída deve ser:
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11E dada a entrada:
Go interfaces are powerfulSua saída deve ser:
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26A principal ideia é que seu CountingReader funcione perfeitamente com io.ReadAll porque ele implementa io.Reader. Qualquer função no ecossistema do Go que aceite um io.Reader funcionará com seu tipo personalizado: esse é o poder do design baseado em interfaces.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// Ler a entrada
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// Remover a quebra de linha final se presente
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: Crie um CountingReader com a string de entrada
// countingReader := NewCountingReader(input)
// TODO: Use io.ReadAll para ler todos os dados do seu CountingReader
// data, err := io.ReadAll(countingReader)
// TODO: Crie um UppercaseWriter
// upperWriter := NewUppercaseWriter()
// TODO: Escreva os dados no seu UppercaseWriter
// upperWriter.Write(data)
// TODO: Imprima os resultados no formato exigido:
// Original: [input]
// Uppercase: [resultado do writer]
// Bytes read: [total de bytes]
// Placeholder para evitar erro de import não utilizado - remover ao implementar
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
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 Arquivos11Biblioteca Padrão e POO
io.Reader e io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRevisão - Modelos de API REST3Ponteiros 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