Menu
CoddyTech

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.

challenge icon

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úc BankAccount với trường Owner được xuất (string) và trường balance khô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ặc Error: [error message]
  • Sau khi gửi tiền: Deposited successfully. New balance: $[balance] hoặc Error: [error message]
  • Sau khi rút tiền: Withdrew successfully. New balance: $[balance] hoặc Error: [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 funds

Với các đầu vào Bob, -50, 25 và 10, đầu ra sẽ là:

Error: initial deposit must be positive

Tự 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
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Go online