Interface Stringer và Error
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 32 trên 107.
Thư viện chuẩn của Go định nghĩa hai interface mà bạn sẽ thường xuyên sử dụng: fmt.Stringer và error. Cả hai đều là interface đơn giản, chỉ có một phương thức, giúp các kiểu dữ liệu của bạn tích hợp mạnh mẽ với các chức năng tích hợp sẵn của Go.
Giao diện Stringer nằm trong gói fmt và chỉ yêu cầu một phương thức:
type Stringer interface {
String() string
}Khi bạn triển khai String() cho kiểu dữ liệu của mình, các hàm như fmt.Println sẽ tự động dùng hàm này để hiển thị giá trị của bạn:
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice (30 tuổi)
}Giao diện error cũng đơn giản không kém, chỉ cần một phương thức Error() trả về một chuỗi:
type error interface {
Error() string
}Bất kỳ kiểu dữ liệu nào có phương thức Error() đều có thể dùng làm giá trị lỗi trong Go. Nhờ vậy, bạn có thể tạo các kiểu lỗi tùy chỉnh kèm thêm ngữ cảnh:
type ValidationError struct {
Field string
Message string
}
func (e ValidationError) Error() string {
return fmt.Sprintf("%s: %s", e.Field, e.Message)
}
func main() {
err := ValidationError{Field: "email", Message: "invalid format"}
fmt.Println(err) // email: invalid format
}Hai giao diện này thể hiện triết lý của Go: các giao diện nhỏ, tập trung, mở ra những hành vi mạnh mẽ thông qua việc triển khai ngầm.
Thử thách
DễHãy xây dựng một hệ thống theo dõi nhiệt độ sử dụng cả hai giao diện Stringer và error để hiển thị thông tin dễ đọc và xử lý lỗi rõ ràng.
Bạn sẽ tổ chức code trong hai tệp:
temperature.go: Tạo một structTemperaturecó các trườngValue(float64) vàUnit(string). Triển khai phương thứcString()để khi in ra, nhiệt độ được hiển thị theo định dạng dễ đọc. Đồng thời tạo một structTemperatureErrorcó các trườngTemp(float64) vàReason(string), rồi triển khai phương thứcError()để cung cấp thông báo lỗi mô tả rõ ràng.main.go: Tạo một hàm có tênValidateTemperaturenhận mộtTemperaturevà trả về lỗi nếu nhiệt độ không hợp lệ. Nhiệt độ không hợp lệ nếu thấp hơn độ không tuyệt đối (-273.15 đối với Celsius, -459.67 đối với Fahrenheit). Nếu hợp lệ, hàm trả vềnil. Đọc dữ liệu nhiệt độ từ đầu vào, tạo một Temperature, kiểm tra tính hợp lệ rồi in kết quả phù hợp.
Đầu vào sẽ gồm:
- Dòng 1: Giá trị nhiệt độ (số thực)
- Dòng 2: Đơn vị (
Ccho Celsius hoặcFcho Fahrenheit)
Phương thức String() của bạn cần trả về định dạng:
[Value] degrees [Unit]Trong đó, Unit là từ đầy đủ "Celsius" hoặc "Fahrenheit", tùy theo đầu vào.
Phương thức Error() của bạn cần trả về định dạng:
invalid temperature [Temp]: [Reason]Nếu nhiệt độ hợp lệ, hãy in Temperature bằng fmt.Println (phương thức này sẽ sử dụng phương thức String() của bạn). Nếu nhiệt độ không hợp lệ, hãy in lỗi (phương thức này sẽ sử dụng phương thức Error() của bạn).
Ví dụ, với 25.5 và C, đầu ra sẽ là:
25.5 degrees CelsiusVới -500 và F, đầu ra sẽ là:
invalid temperature -500: below absolute zeroDùng "below absolute zero" làm lý do khi nhiệt độ quá thấp.
Tự làm thử
package main
import (
"fmt"
)
// ValidateTemperature kiểm tra nhiệt độ có hợp lệ không
// Trả về lỗi nếu nhiệt độ thấp hơn độ không tuyệt đối
// Độ không tuyệt đối: -273.15 theo thang Celsius, -459.67 theo thang Fahrenheit
func ValidateTemperature(t Temperature) error {
// TODO: Triển khai logic kiểm tra tính hợp lệ
// Kiểm tra nhiệt độ có thấp hơn độ không tuyệt đối theo đơn vị tương ứng không
// Trả về TemperatureError nếu không hợp lệ, nil nếu hợp lệ
return nil
}
func main() {
var value float64
var unit string
fmt.Scanln(&value)
fmt.Scanln(&unit)
// TODO: Tạo struct Temperature với các giá trị đầu vào
// TODO: Kiểm tra nhiệt độ bằng ValidateTemperature
// TODO: Nếu có lỗi, in lỗi ra
// Nếu không, in nhiệt độ ra (sử dụng phương thức String())
}
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ệp3Con 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