Модульні уроки (4/5)
Інтерфейс Stringer
fmt.Stringer, ймовірно, найбільш використовуваний стандартний інтерфейс у всіх
Стандартна бібліотека Go. Це ідеальна демонстрація сили
малих інтерфейсів.
Визначення
type Stringer interface {
String() string
}Єдиний підпис. Будь-який тип, який визначає String() string, задовольняє
це. І fmt розпізнає цей інтерфейс: коли ви передаєте значення
fmt.Print*, якщо він реалізує Stringer, результат String()
використовується замість представлення за замовчуванням.
приклад
type Point struct{ X, Y int }
func (p Point) String() string {
return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}
fmt.Println(Point{1, 2}) // (1,2)
fmt.Printf("%v %s\n", Point{3, 4}, Point{5, 6})
// (3,4) (5,6)Те саме значення друкується зі спеціальним форматуванням у %v, %s, Println,
Sprint, log і т. д. Все, просто визначивши один метод.
Коли це має сенс
- Типи, які моделюють домен (ідентифікатори, коди, координати, перерахування).
- Помилки зі зрозумілим для людини представленням (див. наступний урок на
error). - Типи, які ви часто входите в журнал і для яких вам потрібен фіксований формат.
Не для всіх: якщо тип простий і представлення за замовчуванням добре, не додавайте шуму.
Типи переліків: загальний шаблон із iota
type Status int
const (
OK Status = iota
WARN
ERR
)
func (s Status) String() string {
switch s {
case OK: return "OK"
case WARN: return "WARN"
case ERR: return "ERR"
default: return "UNKNOWN"
}
}
fmt.Println(OK, WARN, ERR) // OK WARN ERRБез String() fmt.Println(OK) надрукував би 0. З String(),
він друкує символічне ім'я. stringer
інструмент генерує цей шаблон автоматично.
Слідкуйте за нескінченною рекурсією
type T int
func (t T) String() string {
return fmt.Sprintf("%d", int(t)) // OK: cast to int breaks the recursion
}Стрінгер на стрілочній коробці
Якщо String() має приймач вказівника, лише *T задовольняє Stringer.
За допомогою fmt.Println(t) (значення) Go переглядає набір методів T,
не знаходить його та використовує стандартний формат. Щоб уникнути цього, value
приймач зазвичай використовується для String() (це "перегляд"; він не
змінити будь-що).
Спробуйте
Реалізуйте String() на Point, щоб він повертав (X,Y).
Показати підказку
Для форматування полів використовуйте `fmt.Sprintf`.
Рішення доступне після 3 спроб
Реалізуйте String() на типі Status (int): 0 → «OK», 1 → «WARN», інакше → «ERR».
Показати підказку
Увімкніть значення приймача; не забудьте вказати значення за замовчуванням.
Рішення доступне після 3 спроб
Який підпис повинен мати метод, щоб задовольнити fmt.Stringer?
func (x T) ??? {}Підведення підсумків
fmt.Stringer = interface { String() string }.- Його впровадження змінює те, як
fmt.Print*представляє тип. - Ідіоматичні для переліків (
iota), типів доменів, кодів/ідентифікаторів. - Ніколи не викликайте
fmt.Sprintf("%s", t)наtвсерединіString()(рекурсія). — Угода: приймач значення дляString()(без мутацій, уникає «сюрпризів набору методів»).