Interfaces Stringer y Error
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 32 de 107.
La biblioteca estándar de Go define dos interfaces que usarás constantemente: fmt.Stringer y error. Ambas son interfaces sencillas de un solo método que otorgan a tus tipos una potente integración con la funcionalidad integrada de Go.
La interfaz Stringer reside en el paquete fmt y requiere solo un método:
type Stringer interface {
String() string
}Cuando implementas String() en tu tipo, funciones como fmt.Println lo usan automáticamente para mostrar tu 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 años)
}La interfaz error es igualmente sencilla, ya que requiere un método Error() que devuelve una cadena:
type error interface {
Error() string
}Cualquier tipo con un método Error() puede ser utilizado como un valor de error en Go. Esto permite crear tipos de error personalizados con 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 dos interfaces demuestran la filosofía de Go: interfaces pequeñas y enfocadas que desbloquean un comportamiento potente a través de la implementación implícita.
Desafío
FácilVamos a construir un sistema de monitoreo de temperatura que utiliza las interfaces Stringer y error para proporcionar una salida legible y un manejo de errores significativo.
Organizarás tu código en dos archivos:
temperature.go: Crea una estructuraTemperaturecon los camposValue(float64) yUnit(string). Implementa el métodoString()para que, al imprimirse, muestre la temperatura en un formato legible para humanos. También crea una estructuraTemperatureErrorcon los camposTemp(float64) yReason(string), e implementa el métodoError()para proporcionar mensajes de error descriptivos.main.go: Crea una función llamadaValidateTemperatureque reciba unaTemperaturey devuelva un error si la temperatura no es válida. Una temperatura no es válida si está por debajo del cero absoluto (-273.15 para Celsius, -459.67 para Fahrenheit). Si es válida, la función devuelvenil. Lee los datos de temperatura de la entrada, crea una Temperature, valídala e imprime la salida apropiada.
Se proporcionarán las siguientes entradas:
- Línea 1: Valor de la temperatura (float)
- Línea 2: Unidad (
Cpara Celsius oFpara Fahrenheit)
Tu método String() debe devolver el formato:
[Value] degrees [Unit]Donde Unit es la palabra completa "Celsius" o "Fahrenheit" basada en la entrada.
Tu método Error() debe devolver el formato:
invalid temperature [Temp]: [Reason]Si la temperatura es válida, imprime la Temperature usando fmt.Println (que utilizará tu método String()). Si no es válida, imprime el error (que utilizará tu método Error()).
Por ejemplo, dados 25.5 y C, tu salida debería ser:
25.5 degrees CelsiusDados -500 y F, tu salida debería ser:
invalid temperature -500: below absolute zeroUsa "below absolute zero" como la razón (reason) cuando la temperatura sea demasiado baja.
Hoja de referencia
La biblioteca estándar de Go define dos interfaces esenciales: fmt.Stringer y error.
La interfaz Stringer
La interfaz Stringer del paquete fmt requiere un método:
type Stringer interface {
String() string
}Cuando implementas String() en tu tipo, funciones como fmt.Println lo utilizan automáticamente:
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)
}La interfaz error
La interfaz error requiere un método Error() que devuelve una cadena:
type error interface {
Error() string
}Cualquier tipo con un método Error() puede ser utilizado como un valor de error, permitiendo tipos de error personalizados con 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
}Pruébalo tú mismo
package main
import (
"fmt"
)
// ValidateTemperature comprueba si una temperatura es válida
// Devuelve un error si la temperatura es inferior al cero absoluto
// Cero absoluto: -273.15 para Celsius, -459.67 para Fahrenheit
func ValidateTemperature(t Temperature) error {
// TODO: Implementar la lógica de validación
// Comprobar si la temperatura es inferior al cero absoluto según la unidad
// Devolver un TemperatureError si no es válida, nil si es válida
return nil
}
func main() {
var value float64
var unit string
fmt.Scanln(&value)
fmt.Scanln(&unit)
// TODO: Crear una estructura Temperature con los valores de entrada
// TODO: Validar la temperatura usando ValidateTemperature
// TODO: Si hay un error, imprimir el error
// De lo contrario, imprimir la temperatura (que usará el método String())
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool