Sentinel-Fehler
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 55 von 107.
Ein Sentinelfehler ist eine vordefinierte, paketweite Fehlervariable, die eine bestimmte Fehlerbedingung darstellt. Im Gegensatz dazu, jedes Mal mit errors.New neue Fehler zu erstellen, werden Sentinelfehler einmal deklariert und im gesamten Code wiederverwendet.
Du definierst Sentinel-Fehler als Variablen auf Paketebene, typischerweise mit Namen, die mit Err beginnen:
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)Funktionen geben diese vordefinierten Fehler zurück, wenn bestimmte Bedingungen eintreten:
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}Die Stärke von Sentinel-Fehlern besteht darin, dass Aufrufer auf bestimmte Fehlerbedingungen prüfen können, indem sie Fehlerwerte direkt vergleichen:
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}Die Standardbibliothek von Go verwendet häufig Sentinel-Fehler. Beispielsweise signalisiert io.EOF das Ende der Eingabe, und sql.ErrNoRows weist auf ein leeres Abfrageergebnis hin. Dieses Muster schafft einen klaren Vertrag zwischen Paketen und ihren Benutzern darüber, welche Fehler zu erwarten sind.
Einfache Gleichheitsprüfungen schlagen jedoch fehl, wenn Fehler verpackt (wrapped) sind. In der nächsten Lektion lernen Sie, wie errors.Is() dieses Problem löst, indem es die gesamte Fehlerkette überprüft.
Aufgabe
EinfachErstellen wir ein Bestandsverwaltungssystem, das Sentinel-Fehler verwendet, um bestimmte Fehlerbedingungen zu kommunizieren. Du definierst Fehler-Variablen auf Paketebene, die Aufrufer prüfen können, um verschiedene Szenarien angemessen zu behandeln.
Du organisierst deinen Code auf zwei Dateien verteilt:
inventory.go: Erstelle ein Bestandssystem mit vordefinierten Sentinel-Fehlern für häufige Fehlerfälle.Definiere drei Sentinel-Fehler auf Paketebene:
ErrItemNotFoundmit der Meldung"item not found in inventory"ErrOutOfStockmit der Meldung"item is out of stock"ErrInsufficientQuantitymit der Meldung"insufficient quantity available"
Erstelle eine
Inventory-Struktur, die eine Map mit Artikelnamen und ihren Mengen enthält. Implementiere diese Methoden:NewInventory() *Inventory– erstellt einen leeren BestandAddItem(name string, quantity int)– fügt die Menge eines Artikels hinzu oder aktualisiert sieGetQuantity(name string) (int, error)– gibt die Menge eines Artikels zurück oderErrItemNotFound, wenn er nicht existiertPurchase(name string, quantity int) error– versucht, Artikel zu kaufen. GibtErrItemNotFoundzurück, wenn der Artikel nicht existiert,ErrOutOfStock, wenn die aktuelle Menge null ist, oderErrInsufficientQuantity, wenn die angeforderte Menge den verfügbaren Bestand überschreitet. Bei Erfolg wird der Bestand reduziert undnilzurückgegeben.
main.go: Lies Bestandsoperationen aus der Eingabe und demonstriere, wie Aufrufer mithilfe eines direkten Gleichheitsvergleichs auf bestimmte Sentinel-Fehler prüfen können.Lies den Artikelnamen und die Kaufmenge aus der Eingabe. Erstelle einen Bestand, füge den Artikel
"laptop"mit der Menge5hinzu und versuche anschließend, den Kauf durchzuführen. Gib abhängig vom zurückgegebenen Fehler eine bestimmte Meldung aus, die zeigt, dass du erkannt hast, welcher Sentinel-Fehler aufgetreten ist.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Artikelname, der gekauft werden soll
- Zeile 2: Kaufmenge
Behandle das Ergebnis, indem du prüfst, welcher bestimmte Fehler zurückgegeben wurde:
- Wenn
err == ErrItemNotFound: GibError: Item '[name]' does not exist in our inventoryaus - Wenn
err == ErrOutOfStock: GibError: Item '[name]' is currently out of stockaus - Wenn
err == ErrInsufficientQuantity: GibError: Cannot purchase [quantity] units of '[name]' - not enough in stockaus - Bei Erfolg: Gib
Successfully purchased [quantity] units of '[name]'aus
Wenn beispielsweise laptop und 3 eingegeben werden, sollte deine Ausgabe wie folgt aussehen:
Successfully purchased 3 units of 'laptop'Bei der Eingabe von phone und 2 sollte deine Ausgabe wie folgt aussehen:
Error: Item 'phone' does not exist in our inventoryBei der Eingabe von laptop und 10 sollte deine Ausgabe wie folgt aussehen:
Error: Cannot purchase 10 units of 'laptop' - not enough in stockProbier es selbst
package main
import (
"fmt"
)
func main() {
// Eingabe lesen
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// Inventar erstellen und "laptop" mit der Menge 5 hinzufügen
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: Versuchen, den Artikel zu kaufen
// inv.Purchase(itemName, quantity) aufrufen
// TODO: Überprüfen, welcher spezifische Sentinel-Fehler mittels direktem Gleichheitsvergleich zurückgegeben wurde
// if/else if-Anweisungen verwenden, um Folgendes zu prüfen:
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (Erfolg)
// TODO: Die entsprechende Nachricht basierend auf dem Fehler ausgeben
// Wenn ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// Wenn ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// Wenn ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// Wenn Erfolg: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler