Interfejs błędu
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 52 z 107.
W Go błędy są obsługiwane za pomocą wbudowanego interfejsu o nazwie error. Ten interfejs jest wyjątkowo prosty. Wymaga tylko jednej metody:
type error interface {
Error() string
}Każdy typ, który implementuje metodę Error() zwracającą ciąg znaków, automatycznie spełnia interfejs error. Tak działa system interfejsów w Go: nie jest potrzebna jawna deklaracja.
Najczęstszym sposobem tworzenia błędów jest użycie pakietu 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
}Funkcje w Go zwykle zwracają błąd jako ostatnią wartość zwracaną. Gdy nie wystąpi błąd, zwracasz nil. Następnie wywołujący sprawdza, czy błąd jest różny od nil, zanim będzie kontynuować:
result, err := Divide(10, 0)
if err != nil {
fmt.Println("Error:", err.Error())
return
}
fmt.Println("Result:", result)Możesz również użyć fmt.Errorf, aby tworzyć sformatowane komunikaty o błędach:
func GetUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("invalid user id: %d", id)
}
return "Alice", nil
}Ten wzorzec zwracania błędów zamiast zgłaszania wyjątków jest fundamentalny dla Go. Sprawia, że obsługa błędów jest jawna, i zmusza cię do zastanowienia się, co się dzieje, gdy coś pójdzie nie tak — to kluczowy aspekt pisania odpornego kodu obiektowego.
Wyzwanie
ŁatwyZbudujmy system konta bankowego, który demonstruje sposób obsługi błędów w Go. Utworzysz proste konto, które weryfikuje operacje i zwraca zrozumiałe błędy, gdy coś pójdzie nie tak.
Rozmieścisz kod w dwóch plikach:
account.go: Utwórz strukturęBankAccountz eksportowanym polemOwner(string) i nieeksportowanym polembalance(float64). Zaimplementuj następujące elementy:NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error)— konstruktor, który zwraca błąd z komunikatem"initial deposit must be positive", jeśli początkowa wpłata wynosi zero lub jest ujemna. W przeciwnym razie utwórz i zwróć konto.Deposit(amount float64) error— dodaje pieniądze do salda. Zwróć błąd z komunikatem"deposit amount must be positive", jeśli kwota wynosi zero lub jest ujemna.Withdraw(amount float64) error— odejmuje pieniądze od salda. Zwróć błąd z komunikatem"withdrawal amount must be positive", jeśli kwota wynosi zero lub jest ujemna, albo"insufficient funds", jeśli saldo jest niższe niż kwota wypłaty.Balance() float64— zwraca bieżące saldo
main.go: Odczytaj z wejścia dane konta i kwoty transakcji. Spróbuj utworzyć konto i wykonać operacje. Po każdej operacji wypisz komunikat o powodzeniu albo komunikat o błędzie.
Podane zostaną następujące dane wejściowe:
- Wiersz 1: Imię właściciela
- Wiersz 2: Kwota początkowej wpłaty
- Wiersz 3: Kwota wpłaty
- Wiersz 4: Kwota wypłaty
Wypisz wynik każdej operacji:
- Po utworzeniu konta:
Account created for [Owner] with balance: $[balance]lubError: [error message] - Po wpłacie:
Deposited successfully. New balance: $[balance]lubError: [error message] - Po wypłacie:
Withdrew successfully. New balance: $[balance]lubError: [error message]
Formatuj wszystkie wartości salda z dokładnością do dwóch miejsc po przecinku. Jeśli utworzenie konta się nie powiedzie, pomiń pozostałe operacje.
Na przykład dla danych Alice, 100, 50 i 200 wynik powinien wyglądać następująco:
Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient fundsA dla danych Bob, -50, 25 i 10 wynik powinien wyglądać następująco:
Error: initial deposit must be positiveSpróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Wczytaj imię i nazwisko właściciela
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// Wczytaj wpłatę początkową
initialDepositStr, _ := reader.ReadString('\n')
initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)
// Wczytaj kwotę wpłaty
depositStr, _ := reader.ReadString('\n')
depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)
// Wczytaj kwotę wypłaty
withdrawStr, _ := reader.ReadString('\n')
withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)
// TODO: Utwórz nowe konto bankowe za pomocą NewBankAccount
// Jeśli wystąpi błąd, wypisz "Error: [error message]" i zakończ działanie
// Jeśli operacja się powiedzie, wypisz "Account created for [Owner] with balance: $[balance]"
// TODO: Spróbuj wpłacić podaną kwotę
// Jeśli wystąpi błąd, wypisz "Error: [error message]"
// Jeśli operacja się powiedzie, wypisz "Deposited successfully. New balance: $[balance]"
// TODO: Spróbuj wypłacić podaną kwotę
// Jeśli wystąpi błąd, wypisz "Error: [error message]"
// Jeśli operacja się powiedzie, wypisz "Withdrew successfully. New balance: $[balance]"
// Pamiętaj, aby formatować saldo do dwóch miejsc po przecinku za pomocą fmt.Printf("%.2f", balance)
_ = ownerName
_ = initialDeposit
_ = depositAmount
_ = withdrawAmount
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online