Giao diện 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 52 trên 107.
Trong Go, lỗi được xử lý thông qua một giao diện tích hợp sẵn có tên là error. Giao diện này rất đơn giản. Nó chỉ yêu cầu một phương thức:
type error interface {
Error() string
}Bất kỳ kiểu nào triển khai phương thức Error() trả về một chuỗi đều tự động thỏa mãn giao diện error. Đây là cách hệ thống giao diện của Go hoạt động: không cần khai báo tường minh.
Cách phổ biến nhất để tạo lỗi là sử dụng package errors:
import "errors"
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}Các hàm Go thường trả về lỗi làm giá trị trả về cuối cùng. Khi không có lỗi nào xảy ra, bạn trả về nil. Sau đó, bên gọi kiểm tra xem lỗi có khác nil hay không trước khi tiếp tục:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Bạn cũng có thể dùng fmt.Errorf để tạo thông báo lỗi có định dạng:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Mẫu trả về lỗi thay vì ném ngoại lệ là nền tảng của Go. Mẫu này giúp việc xử lý lỗi trở nên rõ ràng và buộc bạn cân nhắc điều gì sẽ xảy ra khi có sự cố: đây là khía cạnh then chốt khi viết code hướng đối tượng vững chắc.
Thử thách
DễHãy xây dựng một hệ thống tài khoản ngân hàng để minh họa cách Go xử lý lỗi. Bạn sẽ tạo một tài khoản đơn giản, có kiểm tra các thao tác và trả về lỗi rõ ràng khi có sự cố.
Bạn sẽ sắp xếp code trong hai tệp:
account.go: Tạo một cấu trúcBankAccountvới trườngOwnerđược xuất (string) và trườngbalancekhông được xuất (float64). Hãy triển khai những mục sau:NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error)- hàm khởi tạo trả về lỗi với thông báo"initial deposit must be positive"nếu khoản tiền gửi ban đầu bằng 0 hoặc âm. Nếu không, hãy tạo và trả về tài khoản.Deposit(amount float64) error- thêm tiền vào số dư. Trả về lỗi với thông báo"deposit amount must be positive"nếu số tiền bằng 0 hoặc âm.Withdraw(amount float64) error- rút tiền khỏi số dư. Trả về lỗi với thông báo"withdrawal amount must be positive"nếu số tiền bằng 0 hoặc âm, hoặc"insufficient funds"nếu số dư thấp hơn số tiền cần rút.Balance() float64- trả về số dư hiện tại
main.go: Đọc thông tin tài khoản và số tiền giao dịch từ đầu vào. Thử tạo tài khoản và thực hiện các thao tác. Sau mỗi thao tác, in ra thông báo thành công hoặc thông báo lỗi.
Đầu vào sẽ gồm các giá trị sau:
- Dòng 1: Tên chủ tài khoản
- Dòng 2: Số tiền gửi ban đầu
- Dòng 3: Số tiền gửi
- Dòng 4: Số tiền rút
Với mỗi thao tác, hãy in ra kết quả:
- Sau khi tạo tài khoản:
Account created for [Owner] with balance: $[balance]hoặcError: [error message] - Sau khi gửi tiền:
Deposited successfully. New balance: $[balance]hoặcError: [error message] - Sau khi rút tiền:
Withdrew successfully. New balance: $[balance]hoặcError: [error message]
Định dạng mọi giá trị số dư với hai chữ số thập phân. Nếu tạo tài khoản không thành công, hãy bỏ qua các thao tác còn lại.
Ví dụ, với các đầu vào Alice, 100, 50 và 200, đầu ra sẽ là:
Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient fundsVới các đầu vào Bob, -50, 25 và 10, đầu ra sẽ là:
Error: initial deposit must be positiveTự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc tên chủ tài khoản
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Đọc số tiền gửi ban đầu
initialDepositStr, _ := reader.ReadString('\n')
initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)
// Đọc số tiền cần gửi
depositStr, _ := reader.ReadString('\n')
depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)
// Đọc số tiền cần rút
withdrawStr, _ := reader.ReadString('\n')
withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)
// TODO: Tạo tài khoản ngân hàng mới bằng NewBankAccount
// Nếu có lỗi, in ra "Error: [error message]" rồi return
// Nếu thành công, in ra "Account created for [Owner] with balance: $[balance]"
// TODO: Thử gửi số tiền cần gửi
// Nếu có lỗi, in ra "Error: [error message]"
// Nếu thành công, in ra "Deposited successfully. New balance: $[balance]"
// TODO: Thử rút số tiền cần rút
// Nếu có lỗi, in ra "Error: [error message]"
// Nếu thành công, in ra "Withdrew successfully. New balance: $[balance]"
// Nhớ định dạng số dư với hai chữ số thập phân bằng fmt.Printf("%.2f", balance)
_ = ownerName
_ = initialDeposit
_ = depositAmount
_ = withdrawAmount
}
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