interface fmt.Stringer
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 75 trên 107.
Giao diện (interface) fmt.Stringer quyết định cách các kiểu dữ liệu của bạn hiển thị khi được in ra. Đây là một trong những giao diện được triển khai phổ biến nhất trong Go, được định nghĩa trong gói fmt:
type Stringer interface {
String() string
}Khi bạn truyền một giá trị vào fmt.Println, fmt.Printf với %v hoặc các hàm tương tự, Go sẽ kiểm tra xem kiểu đó có triển khai Stringer hay không. Nếu có, Go sẽ gọi phương thức String() để lấy biểu diễn dạng văn bản.
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
}Nếu không có phương thức (method) String(), khi in ra sẽ hiển thị định dạng struct mặc định: {23.5}. Bằng cách triển khai Stringer, bạn kiểm soát chính xác cách kiểu dữ liệu của mình hiển thị.
Điều này đặc biệt hữu ích với những kiểu dữ liệu mà các giá trị trường thô không thể hiện rõ ý nghĩa. Kiểu Duration có thể hiển thị là "2h 30m" thay vì hiển thị số nano giây thô, hoặc kiểu User có thể hiển thị tên đã định dạng thay vì tất cả các trường của nó.
Thử thách
DễHãy xây dựng một hệ thống theo dõi thời gian, hiển thị thời lượng theo định dạng dễ hiểu! Bạn sẽ triển khai giao diện fmt.Stringer để kiểm soát cách các kiểu dữ liệu tùy chỉnh hiển thị khi in ra.
Bạn sẽ sắp xếp code trong hai tệp:
duration.go: Định nghĩa các kiểu dữ liệu liên quan đến thời gian với cách biểu diễn chuỗi tùy chỉnh.Tạo một cấu trúc
Durationđể lưu tổng số phút dưới dạng số nguyên. Triển khai phương thứcString()để khi in ra, thời lượng hiển thị theo định dạng dễ đọc như2h 30mcho 150 phút hoặc0h 45mcho 45 phút. Luôn hiển thị cả giờ và phút.Tạo cấu trúc
Taskvới các trườngName(chuỗi) vàTime(Duration). Triển khaiString()cho Task để hiển thị theo định dạng[Name]: [duration], trong đó thời lượng sử dụng phương thức String của chính nó.main.go: Tạo và hiển thị các tác vụ cùng thời lượng của chúng.Đọc số lượng tác vụ, sau đó đọc tên và thời lượng tính bằng phút của từng tác vụ. Tạo các đối tượng Task và in trực tiếp từng đối tượng bằng
fmt.Println: giao diện Stringer sẽ tự động định dạng.Sau khi in tất cả tác vụ, hãy tính và in tổng thời gian của tất cả tác vụ theo định dạng:
Total: [duration]
Đầu vào sẽ có dạng:
- Dòng 1: Số lượng tác vụ
- Các dòng tiếp theo: Tên tác vụ và số phút (hai dòng cho mỗi tác vụ)
Ví dụ, với đầu vào:
3
Code Review
90
Meeting
45
Documentation
120Đầu ra của bạn sẽ là:
Code Review: 1h 30m
Meeting: 0h 45m
Documentation: 2h 0m
Total: 4h 15mVới đầu vào:
2
Design
180
Testing
65Đầu ra của bạn sẽ là:
Design: 3h 0m
Testing: 1h 5m
Total: 4h 5mBằng cách triển khai Stringer, các kiểu dữ liệu của bạn tích hợp liền mạch với các hàm in ra của Go. Thay vì thấy các trường thô của cấu trúc như {90}, bạn sẽ nhận được đầu ra rõ nghĩa, dễ hiểu với người dùng.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc số lượng tác vụ
line, _ := reader.ReadString('\n')
numTasks, _ := strconv.Atoi(strings.TrimSpace(line))
// TODO: Tạo một slice để lưu các tác vụ
// TODO: Theo dõi tổng số phút
for i := 0; i < numTasks; i++ {
// Đọc tên tác vụ
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Đọc thời lượng tác vụ tính bằng phút
minutesLine, _ := reader.ReadString('\n')
minutes, _ := strconv.Atoi(strings.TrimSpace(minutesLine))
// TODO: Tạo một Task với tên và Duration
// TODO: In tác vụ ra (fmt.Println sẽ dùng giao diện Stringer)
// TODO: Cộng minutes vào tổng
_ = name
_ = minutes
}
// TODO: In tổng thời lượng theo định dạng "Total: Xh Ym"
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp11Thư viện chuẩn và OOP
io.Reader và io.Writersort.Interfaceinterface fmt.Stringerencoding/json với structinterface http.HandlerÔn tập: mô hình REST API3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online