Menu
Coddy logo textTech

Интерфейсы Stringer и Error

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 32 из 107.

Стандартная библиотека Go определяет два интерфейса, которые вы будете использовать постоянно: fmt.Stringer и error. Оба являются простыми интерфейсами с одним методом, которые обеспечивают вашим типам мощную интеграцию со встроенными возможностями Go.

Интерфейс Stringer находится в пакете fmt и требует всего один метод:

type Stringer interface {
    String() string
}

Когда вы реализуете String() для вашего типа, такие функции, как fmt.Println, автоматически используют его для отображения вашего значения:

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 лет)
}

Интерфейс error так же прост и требует наличия метода Error(), который возвращает строку:

type error interface {
    Error() string
}

Любой тип с методом Error() может использоваться в качестве значения ошибки в Go. Это позволяет создавать пользовательские типы ошибок с дополнительным контекстом:

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
}

Эти два интерфейса демонстрируют философию Go: небольшие, узкоспециализированные интерфейсы, которые обеспечивают мощную функциональность благодаря неявной реализации.

challenge icon

Задание

Легко

Давайте создадим систему мониторинга температуры, которая использует интерфейсы Stringer и error для обеспечения читаемого вывода и осмысленной обработки ошибок.

Вы организуете свой код в двух файлах:

  • temperature.go: Создайте структуру Temperature с полями Value (float64) и Unit (string). Реализуйте метод String() так, чтобы при выводе на печать он отображал температуру в удобном для человека формате. Также создайте структуру TemperatureError с полями Temp (float64) и Reason (string) и реализуйте метод Error() для предоставления описательных сообщений об ошибках.
  • main.go: Создайте функцию с именем ValidateTemperature, которая принимает Temperature и возвращает ошибку, если температура недействительна. Температура считается недействительной, если она ниже абсолютного нуля (-273.15 для Celsius, -459.67 для Fahrenheit). Если температура валидна, функция возвращает nil. Считайте данные о температуре из входного потока, создайте Temperature, проверьте её и выведите соответствующий результат.

Будут предоставлены следующие входные данные:

  • Строка 1: Значение температуры (float)
  • Строка 2: Единица измерения (C для Celsius или F для Fahrenheit)

Ваш метод String() должен возвращать формат:

[Value] degrees [Unit]

Где Unit — это полное слово "Celsius" или "Fahrenheit" в зависимости от входных данных.

Ваш метод Error() должен возвращать формат:

invalid temperature [Temp]: [Reason]

Если температура валидна, выведите Temperature с помощью fmt.Println (который будет использовать ваш метод String()). Если невалидна, выведите ошибку (которая будет использовать ваш метод Error()).

Например, для 25.5 и C ваш вывод должен быть:

25.5 degrees Celsius

Для -500 и F ваш вывод должен быть:

invalid temperature -500: below absolute zero

Используйте "below absolute zero" в качестве причины (reason), когда температура слишком низкая.

Шпаргалка

Стандартная библиотека Go определяет два важных интерфейса: fmt.Stringer и error.

Интерфейс Stringer

Интерфейс Stringer из пакета fmt требует наличия одного метода:

type Stringer interface {
    String() string
}

Когда вы реализуете метод String() для своего типа, такие функции, как fmt.Println, автоматически используют его:

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)
}

Интерфейс error

Интерфейс error требует наличия метода Error(), который возвращает строку:

type error interface {
    Error() string
}

Любой тип с методом Error() может использоваться в качестве значения ошибки, что позволяет создавать пользовательские типы ошибок с дополнительным контекстом:

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
}

Попробуйте сами

package main

import (
	"fmt"
)

// ValidateTemperature проверяет, является ли температура допустимой
// Возвращает ошибку, если температура ниже абсолютного нуля
// Абсолютный нуль: -273.15 для Celsius, -459.67 для Fahrenheit
func ValidateTemperature(t Temperature) error {
	// TODO: Реализовать логику валидации
	// Проверить, ниже ли температура абсолютного нуля в зависимости от единицы измерения
	// Вернуть TemperatureError, если значение невалидно, и nil, если валидно
	return nil
}

func main() {
	var value float64
	var unit string
	fmt.Scanln(&value)
	fmt.Scanln(&unit)

	// TODO: Создать структуру Temperature с входными значениями

	// TODO: Проверить температуру с помощью ValidateTemperature

	// TODO: Если возникла ошибка, вывести её
	// В противном случае вывести температуру (будет использован метод String())
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование