Błędy wartownicze
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 55 z 107.
Błąd wartowniczy to predefiniowana zmienna błędu na poziomie pakietu, która reprezentuje określony warunek błędu. W przeciwieństwie do tworzenia nowych błędów za każdym razem za pomocą errors.New, błędy wartownicze deklaruje się raz i ponownie wykorzystuje w całym kodzie.
Definiujesz błędy wartownicze jako zmienne na poziomie pakietu, zwykle o nazwach zaczynających się od Err:
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)Funkcje zwracają te predefiniowane błędy, gdy wystąpią określone warunki:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}Zaletą błędów wartowniczych jest to, że wywołujący mogą sprawdzać konkretne warunki błędów, bezpośrednio porównując wartości błędów:
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}Biblioteka standardowa Go intensywnie korzysta z błędów wartowniczych. Na przykład io.EOF sygnalizuje koniec danych wejściowych, a sql.ErrNoRows oznacza pusty wynik zapytania. Ten wzorzec tworzy jasny kontrakt między pakietami a ich użytkownikami dotyczący tego, jakich błędów można się spodziewać.
Jednak proste sprawdzanie równości przestaje działać, gdy błędy są opakowane. W następnej lekcji dowiesz się, jak errors.Is() rozwiązuje ten problem, sprawdzając cały łańcuch błędów.
Wyzwanie
ŁatwyZbudujmy system zarządzania zapasami, który używa błędów wartowniczych do przekazywania informacji o konkretnych warunkach niepowodzenia. Zdefiniujesz zmienne błędów na poziomie pakietu, które wywołujący mogą sprawdzać, aby odpowiednio obsługiwać różne sytuacje.
Rozmieścisz swój kod w dwóch plikach:
inventory.go: Utwórz system zarządzania zapasami z predefiniowanymi błędami wartowniczymi dla typowych przypadków niepowodzenia.Zdefiniuj trzy błędy wartownicze na poziomie pakietu:
ErrItemNotFoundz komunikatem"item not found in inventory"ErrOutOfStockz komunikatem"item is out of stock"ErrInsufficientQuantityz komunikatem"insufficient quantity available"
Utwórz strukturę
Inventory, która przechowuje mapę nazw produktów i ich ilości. Zaimplementuj następujące metody:NewInventory() *Inventory– tworzy pusty magazynAddItem(name string, quantity int)– dodaje produkt lub aktualizuje jego ilośćGetQuantity(name string) (int, error)– zwraca ilość produktu alboErrItemNotFound, jeśli produkt nie istniejePurchase(name string, quantity int) error– podejmuje próbę zakupu produktów. ZwróćErrItemNotFound, jeśli produkt nie istnieje,ErrOutOfStock, jeśli bieżąca ilość wynosi zero, lubErrInsufficientQuantity, jeśli żądana ilość przekracza dostępny zapas. W przypadku powodzenia zmniejsz zapas i zwróćnil.
main.go: Odczytaj z wejścia operacje dotyczące zapasów i pokaż, jak wywołujący mogą sprawdzać konkretne błędy wartownicze za pomocą bezpośredniego porównania równości.Odczytaj nazwę produktu i ilość do zakupu z wejścia. Utwórz magazyn, dodaj produkt
"laptop"w ilości5, a następnie podejmij próbę zakupu. Na podstawie zwróconego błędu wyświetl konkretny komunikat informujący, który błąd wartowniczy został rozpoznany.
Dane wejściowe będą następujące:
- Wiersz 1: Nazwa produktu do kupienia
- Wiersz 2: Ilość do kupienia
Obsłuż wynik, sprawdzając, który konkretny błąd został zwrócony:
- Jeśli
err == ErrItemNotFound: wyświetlError: Item '[name]' does not exist in our inventory - Jeśli
err == ErrOutOfStock: wyświetlError: Item '[name]' is currently out of stock - Jeśli
err == ErrInsufficientQuantity: wyświetlError: Cannot purchase [quantity] units of '[name]' - not enough in stock - W przypadku powodzenia: wyświetl
Successfully purchased [quantity] units of '[name]'
Na przykład dla danych laptop i 3 wynik powinien wyglądać tak:
Successfully purchased 3 units of 'laptop'A dla danych phone i 2 wynik powinien wyglądać tak:
Error: Item 'phone' does not exist in our inventoryA dla danych laptop i 10 wynik powinien wyglądać tak:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockSpróbuj swoich sił
package main
import (
"fmt"
)
func main() {
// Odczytaj dane wejściowe
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// Utwórz magazyn i dodaj "laptop" w ilości 5
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: Spróbuj kupić przedmiot
// Wywołaj inv.Purchase(itemName, quantity)
// TODO: Sprawdź, który konkretny błąd sygnalizacyjny został zwrócony, używając bezpośredniego porównania równości
// Użyj instrukcji if/else if, aby sprawdzić:
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (sukces)
// TODO: Wyświetl odpowiedni komunikat w zależności od błędu
// Jeśli ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// Jeśli ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// Jeśli ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// Jeśli operacja się powiedzie: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
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