Bài học theo mô-đun (4/5)
Giao diện Stringer
fmt.Stringer có lẽ là giao diện tiêu chuẩn được sử dụng nhiều nhất trong tất cả các
Thư viện tiêu chuẩn của Go. Đó là sự thể hiện hoàn hảo về sức mạnh
của các giao diện nhỏ.
Sự định nghĩa
type Stringer interface {
String() string
}Một chữ ký duy nhất. Bất kỳ loại nào xác định String() string đều thỏa mãn
nó. Và fmt nhận ra giao diện này: khi bạn chuyển một giá trị tới
fmt.Print*, nếu nó triển khai Stringer, kết quả của String()
được sử dụng thay cho biểu diễn mặc định.
Ví dụ
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)Giá trị tương tự được in với định dạng tùy chỉnh trong %v, %s, Println,
Sprint, log, v.v. Tất cả chỉ bằng cách xác định một phương thức.
Khi nó có ý nghĩa
- Các loại mô hình miền (id, mã, tọa độ, liệt kê).
- Lỗi về cách trình bày mà con người có thể đọc được (xem bài học tiếp theo về
error). - Loại bạn đăng nhập thường xuyên và bạn muốn có định dạng cố định.
Không dành cho tất cả mọi thứ: nếu loại đơn giản và biểu diễn mặc định được thôi, đừng thêm tiếng ồn.
Các loại Enum: mẫu chung với 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 ERRNếu không có String(), fmt.Println(OK) sẽ in 0. Với String(),
nó in tên tượng trưng. stringer
công cụ này tự động tạo ra mẫu này.
Coi chừng đệ quy vô hạn
type T int
func (t T) String() string {
return fmt.Sprintf("%d", int(t)) // OK: cast to int breaks the recursion
}Stringer trên một máy thu con trỏ
Nếu String() có bộ thu con trỏ thì chỉ *T thỏa mãn Stringer.
Với fmt.Println(t) (một giá trị), Go xem xét tập phương thức của T,
không tìm thấy nó và sử dụng định dạng mặc định. Để tránh điều này, giá trị
bộ thu thường được sử dụng cho String() (đó là "chế độ xem"; không phải
sửa đổi bất cứ điều gì).
Hãy thử nó
Triển khai String() trên Point để nó trả về (X,Y).
Hiển thị gợi ý
Sử dụng `fmt.Sprintf` để định dạng các trường.
Giải pháp khả dụng sau 3 lần thử
Triển khai String() trên một loại Trạng thái (int): 0 → 'OK', 1 → 'WARN', nếu không thì → 'ERR'.
Hiển thị gợi ý
Bật giá trị của máy thu; hãy nhớ cung cấp một mặc định.
Giải pháp khả dụng sau 3 lần thử
Phương thức này phải có chữ ký nào để thỏa mãn fmt.Stringer?
func (x T) ??? {}Tóm tắt lại
fmt.Stringer = interface { String() string }.- Việc triển khai nó sẽ thay đổi cách
fmt.Print*thể hiện loại. - Thành ngữ cho enum (
iota), loại tên miền, mã/số nhận dạng. - Không bao giờ gọi
fmt.Sprintf("%s", t)trêntbên trongString()(đệ quy). - Quy ước: bộ nhận giá trị cho
String()(không có đột biến, tránh "bất ngờ về phương thức đặt").