Lekcje modułu (4/5)
Interfejs Stringer
fmt.Stringer jest prawdopodobnie najczęściej używanym standardowym interfejsem we wszystkich
Standardowa biblioteka Go. To idealna demonstracja mocy
małych interfejsów.
Definicja
type Stringer interface {
String() string
}Pojedynczy podpis. Każdy typ definiujący String() string spełnia wymagania
to. A fmt rozpoznaje ten interfejs: kiedy przekazujesz wartość do
fmt.Print*, jeśli implementuje Stringer, wynik String()
jest używany zamiast reprezentacji domyślnej.
Przykład
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)Ta sama wartość jest drukowana z niestandardowym formatowaniem w %v, %s, Println,
Sprint, log, itd. Wszystko poprzez zdefiniowanie jednej metody.
Kiedy ma to sens
- Typy modelujące domenę (identyfikatory, kody, współrzędne, wyliczenia).
- Błędy w reprezentacji czytelnej dla człowieka (patrz następna lekcja na temat
error). - Typy, które często logujesz i dla których chcesz mieć stały format.
Nie do wszystkiego: jeśli typ jest prosty i jest to domyślna reprezentacja jest w porządku, nie dodawaj hałasu.
Typy wyliczeniowe: wspólny wzorzec z 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 ERRBez String(), fmt.Println(OK) wydrukowałoby 0. Z String(),
wypisuje symboliczną nazwę. stringer
narzędzie automatycznie generuje ten wzór.
Uważaj na nieskończoną rekurencję
type T int
func (t T) String() string {
return fmt.Sprintf("%d", int(t)) // OK: cast to int breaks the recursion
}Stringer na odbiorniku wskaźnika
Jeśli String() ma odbiornik wskaźnika, tylko *T spełnia wymagania Stringer.
W przypadku fmt.Println(t) (wartości) Go sprawdza zestaw metod T,
nie znajduje go i używa formatu domyślnego. Aby tego uniknąć, wartość
odbiornik jest zwykle używany dla String() (jest to „widok”; tak nie jest
modyfikować cokolwiek).
Spróbuj
Zaimplementuj String() w punkcie, aby zwracał (X, Y).
Pokaż wskazówkę
Użyj `fmt.Sprintf`, aby sformatować pola.
Rozwiązanie dostępne po 3 próbach
Zaimplementuj String() dla typu Status (int): 0 → „OK”, 1 → „WARN”, w przeciwnym razie → „ERR”.
Pokaż wskazówkę
Włącz wartość odbiornika; pamiętaj o podaniu wartości domyślnej.
Rozwiązanie dostępne po 3 próbach
Jaki podpis musi posiadać metoda, aby spełnić wymagania fmt.Stringer?
func (x T) ??? {}Podsumowanie
- KODEF0.
- Implementacja zmienia sposób, w jaki
fmt.Print*reprezentuje typ. - Idiomatyczny dla wyliczeń (
iota), typów domen, kodów/identyfikatorów. - Nigdy nie wywołuj
fmt.Sprintf("%s", t)natwewnątrzString()(rekurencja). - Konwencja: odbiorca wartości dla
String()(bez mutacji, pozwala uniknąć „niespodzianek związanych z zestawem metod”).