Interfaces Stringer & Error
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 32 de 107.
A biblioteca padrão do Go define duas interfaces que você usará constantemente: fmt.Stringer e error. Ambas são interfaces simples de método único que dão aos seus tipos uma integração poderosa com a funcionalidade nativa do Go.
A interface Stringer reside no pacote fmt e requer apenas um método:
type Stringer interface {
String() string
}Quando você implementa String() em seu tipo, funções como fmt.Println automaticamente o utilizam para exibir seu valor:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30 anos de idade)
}A interface error é igualmente simples, exigindo um método Error() que retorna uma string:
type error interface {
Error() string
}Qualquer tipo com um método Error() pode ser usado como um valor de erro em Go. Isso permite que você crie tipos de erro personalizados com contexto adicional:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: invalid format
}Estas duas interfaces demonstram a filosofia do Go: interfaces pequenas e focadas que desbloqueiam comportamentos poderosos através de implementação implícita.
Desafio
FácilVamos construir um sistema de monitoramento de temperatura que utiliza as interfaces Stringer e error para fornecer uma saída legível e um tratamento de erros significativo.
Você organizará seu código em dois arquivos:
temperature.go: Crie uma structTemperaturecom os camposValue(float64) eUnit(string). Implemente o métodoString()para que, quando impresso, exiba a temperatura em um formato legível por humanos. Também crie uma structTemperatureErrorcom os camposTemp(float64) eReason(string), e implemente o métodoError()para fornecer mensagens de erro descritivas.main.go: Crie uma função chamadaValidateTemperatureque recebe umaTemperaturee retorna um erro se a temperatura for inválida. Uma temperatura é inválida se estiver abaixo do zero absoluto (-273.15 para Celsius, -459.67 para Fahrenheit). Se for válida, a função retornanil. Leia os dados de temperatura da entrada, crie uma Temperature, valide-a e imprima a saída apropriada.
As seguintes entradas serão fornecidas:
- Linha 1: Valor da temperatura (float)
- Linha 2: Unidade (
Cpara Celsius ouFpara Fahrenheit)
Seu método String() deve retornar o formato:
[Value] degrees [Unit]Onde Unit é a palavra completa "Celsius" ou "Fahrenheit" baseada na entrada.
Seu método Error() deve retornar o formato:
invalid temperature [Temp]: [Reason]Se a temperatura for válida, imprima a Temperature usando fmt.Println (que usará seu método String()). Se for inválida, imprima o erro (que usará seu método Error()).
Por exemplo, dados 25.5 e C, sua saída deve ser:
25.5 degrees CelsiusDados -500 e F, sua saída deve ser:
invalid temperature -500: below absolute zeroUse "below absolute zero" como a razão (reason) quando a temperatura for muito baixa.
Folha de consulta
A biblioteca padrão do Go define duas interfaces essenciais: fmt.Stringer e error.
A Interface Stringer
A interface Stringer do pacote fmt requer um método:
type Stringer interface {
String() string
}Quando você implementa String() no seu tipo, funções como fmt.Println o utilizam automaticamente:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30 years old)
}A Interface error
A interface error requer um método Error() que retorna uma string:
type error interface {
Error() string
}Qualquer tipo com um método Error() pode ser usado como um valor de erro, permitindo tipos de erro personalizados com contexto adicional:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: invalid format
}Experimente você mesmo
package main
import (
"fmt"
)
// ValidateTemperature verifica se uma temperatura é válida
// Retorna um erro se a temperatura estiver abaixo do zero absoluto
// Zero absoluto: -273.15 para Celsius, -459.67 para Fahrenheit
func ValidateTemperature(t Temperature) error {
// TODO: Implementar lógica de validação
// Verificar se a temperatura está abaixo do zero absoluto com base na unidade
// Retornar um TemperatureError se for inválida, nil se for válida
return nil
}
func main() {
var value float64
var unit string
fmt.Scanln(&value)
fmt.Scanln(&unit)
// TODO: Criar uma struct Temperature com os valores de entrada
// TODO: Validar a temperatura usando ValidateTemperature
// TODO: Se houver um erro, imprimir o erro
// Caso contrário, imprimir a temperatura (que usará o método String())
}
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