Интерфейс fmt.Stringer
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 75 из 107.
Интерфейс fmt.Stringer управляет тем, как ваши типы отображаются при выводе на печать. Это один из наиболее часто реализуемых интерфейсов в Go, определенный в пакете fmt:
type Stringer interface {
String() string
}Когда вы передаете значение в fmt.Println, fmt.Printf с %v или аналогичные функции, Go проверяет, реализует ли тип Stringer. Если да, Go вызывает метод String() для получения текстового представления.
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}Без метода String() вывод будет отображать формат структуры по умолчанию: {23.5}. Реализуя Stringer, вы точно контролируете то, как ваш тип представляет себя.
Это особенно полезно для типов, в которых необработанные значения полей не передают смысл четко. Тип Duration может отображаться как "2h 30m" вместо показа необработанных наносекунд, или тип User может показывать отформатированное имя вместо всех его полей.
Задание
ЛегкоДавайте создадим систему учета времени, которая отображает длительность в удобном для человека формате! Вы реализуете интерфейс fmt.Stringer, чтобы управлять тем, как ваши пользовательские типы выглядят при выводе на печать.
Вы организуете свой код в двух файлах:
duration.go: Определите типы, связанные со временем, с пользовательскими строковыми представлениями.Создайте структуру
Duration, которая хранит общее количество минут в виде целого числа. Реализуйте методString()так, чтобы при печати он отображался в читаемом формате, например2h 30mдля 150 минут или0h 45mдля 45 минут. Часы и минуты должны отображаться всегда.Создайте структуру
Taskс полямиName(string) иTime(Duration). РеализуйтеString()для Task, чтобы отображать данные в формате[Name]: [duration], где длительность использует свой собственный метод String.main.go: Создавайте и отображайте задачи с их длительностью.Считайте количество задач, затем для каждой задачи считайте её название и длительность в минутах. Создайте экземпляры Task и напечатайте каждый из них напрямую с помощью
fmt.Println— интерфейс Stringer автоматически позаботится о форматировании.После вывода всех задач вычислите и напечатайте общее время всех задач в формате:
Total: [duration]
Будут предоставлены следующие входные данные:
- Строка 1: Количество задач
- Следующие строки: Название задачи и минуты (две строки на задачу)
Например, для входных данных:
3
Code Review
90
Meeting
45
Documentation
120Ваш вывод должен быть таким:
Code Review: 1h 30m
Meeting: 0h 45m
Documentation: 2h 0m
Total: 4h 15mИ для входных данных:
2
Design
180
Testing
65Ваш вывод должен быть таким:
Design: 3h 0m
Testing: 1h 5m
Total: 4h 5mБлагодаря реализации Stringer ваши типы легко интегрируются с функциями печати Go. Вместо того чтобы видеть необработанные поля структуры, такие как {90}, вы получаете осмысленный вывод, понятный пользователям.
Шпаргалка
Интерфейс fmt.Stringer управляет тем, как типы отображаются при выводе на печать:
type Stringer interface {
String() string
}Когда вы передаете значение в fmt.Println или fmt.Printf с использованием %v, Go проверяет, реализует ли тип интерфейс Stringer. Если да, Go вызывает метод String() для получения текстового представления.
Пример реализации:
type Temperature struct {
Celsius float64
}
func (t Temperature) String() string {
return fmt.Sprintf("%.1f°C", t.Celsius)
}
func main() {
temp := Temperature{Celsius: 23.5}
fmt.Println(temp) // 23.5°C
}Без метода String() при печати будет отображаться формат структуры по умолчанию: {23.5}. Реализуя Stringer, вы точно контролируете, как ваш тип представляет себя, делая вывод более осмысленным и удобным для пользователя.
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Прочитать количество задач
line, _ := reader.ReadString('\n')
numTasks, _ := strconv.Atoi(strings.TrimSpace(line))
// TODO: Создать слайс для хранения задач
// TODO: Отслеживать общее количество минут
for i := 0; i < numTasks; i++ {
// Прочитать название задачи
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Прочитать длительность задачи в минутах
minutesLine, _ := reader.ReadString('\n')
minutes, _ := strconv.Atoi(strings.TrimSpace(minutesLine))
// TODO: Создать Task с именем и Duration
// TODO: Вывести задачу (fmt.Println будет использовать интерфейс Stringer)
// TODO: Добавить минуты к общему количеству
_ = name
_ = minutes
}
// TODO: Вывести общую длительность в формате "Total: Xh Ym"
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool