Erreurs sentinelles
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 55 sur 107.
Une erreur sentinelle est une variable d'erreur prédéfinie au niveau du package qui représente une condition d'erreur spécifique. Contrairement à la création de nouvelles erreurs à chaque fois avec errors.New, les erreurs sentinelles sont déclarées une seule fois et réutilisées dans tout votre code.
Vous définissez les erreurs sentinelles comme des variables au niveau du package, généralement avec des noms commençant par Err :
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)Les fonctions renvoient ces erreurs prédéfinies lorsque des conditions spécifiques surviennent :
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}La puissance des erreurs sentinelles réside dans le fait que les appelants peuvent vérifier des conditions d'erreur spécifiques en comparant directement les valeurs d'erreur :
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}La bibliothèque standard de Go utilise intensivement les erreurs sentinelles. Par exemple, io.EOF signale la fin de l'entrée, et sql.ErrNoRows indique un résultat de requête vide. Ce modèle crée un contrat clair entre les packages et leurs utilisateurs sur les erreurs à attendre.
Cependant, les vérifications d'égalité simples échouent lorsque les erreurs sont enveloppées. Dans la prochaine leçon, vous apprendrez comment errors.Is() résout ce problème en vérifiant toute la chaîne d'erreurs.
Défi
FacileConstruisons un système de gestion d'inventaire qui utilise des erreurs sentinelles pour communiquer des conditions d'échec spécifiques. Vous définirez des variables d'erreur au niveau du package que les appelants pourront vérifier pour gérer différents scénarios de manière appropriée.
Vous organiserez votre code sur deux fichiers :
inventory.go: Créez un système d'inventaire avec des erreurs sentinelles prédéfinies pour les cas d'échec courants.Définissez trois erreurs sentinelles au niveau du package :
ErrItemNotFoundavec le message"item not found in inventory"ErrOutOfStockavec le message"item is out of stock"ErrInsufficientQuantityavec le message"insufficient quantity available"
Créez une structure
Inventoryqui contient une map associant des noms d'articles à leurs quantités. Implémentez ces méthodes :NewInventory() *Inventory- crée un inventaire videAddItem(name string, quantity int)- ajoute ou met à jour la quantité d'un articleGetQuantity(name string) (int, error)- retourne la quantité d'un article, ouErrItemNotFounds'il n'existe pasPurchase(name string, quantity int) error- tente d'acheter des articles. RetourneErrItemNotFoundsi l'article n'existe pas,ErrOutOfStocksi la quantité actuelle est de zéro, ouErrInsufficientQuantitysi la quantité demandée dépasse le stock disponible. En cas de succès, réduisez l'inventaire et retourneznil.
main.go: Lisez les opérations d'inventaire depuis l'entrée et démontrez comment les appelants peuvent vérifier des erreurs sentinelles spécifiques en utilisant une comparaison d'égalité directe.Lisez le nom de l'article et la quantité d'achat depuis l'entrée. Créez un inventaire, ajoutez l'article
"laptop"avec une quantité de5, puis tentez l'achat. En fonction de l'erreur retournée, affichez un message spécifique montrant que vous avez identifié quelle erreur sentinelle s'est produite.
Les entrées suivantes seront fournies :
- Ligne 1 : Nom de l'article à acheter
- Ligne 2 : Quantité à acheter
Gérez le résultat en vérifiant quelle erreur spécifique a été retournée :
- Si
err == ErrItemNotFound: affichezError: Item '[name]' does not exist in our inventory - Si
err == ErrOutOfStock: affichezError: Item '[name]' is currently out of stock - Si
err == ErrInsufficientQuantity: affichezError: Cannot purchase [quantity] units of '[name]' - not enough in stock - En cas de succès : affichez
Successfully purchased [quantity] units of '[name]'
Par exemple, avec laptop et 3, votre sortie devrait être :
Successfully purchased 3 units of 'laptop'Et avec phone et 2, votre sortie devrait être :
Error: Item 'phone' does not exist in our inventoryEt avec laptop et 10, votre sortie devrait être :
Error: Cannot purchase 10 units of 'laptop' - not enough in stockAide-mémoire
Une erreur sentinelle est une variable d'erreur prédéfinie au niveau du package qui représente une condition d'erreur spécifique. Les erreurs sentinelles sont déclarées une seule fois et réutilisées dans tout votre code.
Définissez les erreurs sentinelles comme des variables au niveau du package, généralement avec des noms commençant par Err :
package user
import "errors"
var (
ErrNotFound = errors.New("user not found")
ErrInvalidEmail = errors.New("invalid email address")
ErrDuplicate = errors.New("user already exists")
)Les fonctions renvoient ces erreurs prédéfinies lorsque des conditions spécifiques surviennent :
func FindUser(id int) (*User, error) {
user, exists := users[id]
if !exists {
return nil, ErrNotFound
}
return user, nil
}Les appelants peuvent vérifier des conditions d'erreur spécifiques en comparant directement les valeurs d'erreur :
user, err := FindUser(42)
if err == ErrNotFound {
fmt.Println("Creating new user...")
} else if err != nil {
fmt.Println("Unexpected error:", err)
}La bibliothèque standard de Go utilise largement les erreurs sentinelles, telles que io.EOF et sql.ErrNoRows.
Essayez vous-même
package main
import (
"fmt"
)
func main() {
// Lire l'entrée
var itemName string
var quantity int
fmt.Scanln(&itemName)
fmt.Scanln(&quantity)
// Créer l'inventaire et ajouter "laptop" avec une quantité de 5
inv := NewInventory()
inv.AddItem("laptop", 5)
// TODO: Tenter d'acheter l'article
// Appeler inv.Purchase(itemName, quantity)
// TODO: Vérifier quelle erreur sentinelle spécifique a été retournée en utilisant une comparaison d'égalité directe
// Utiliser des instructions if/else if pour vérifier :
// - err == ErrItemNotFound
// - err == ErrOutOfStock
// - err == ErrInsufficientQuantity
// - err == nil (succès)
// TODO: Afficher le message approprié en fonction de l'erreur
// Si ErrItemNotFound: fmt.Printf("Error: Item '%s' does not exist in our inventory\n", itemName)
// Si ErrOutOfStock: fmt.Printf("Error: Item '%s' is currently out of stock\n", itemName)
// Si ErrInsufficientQuantity: fmt.Printf("Error: Cannot purchase %d units of '%s' - not enough in stock\n", quantity, itemName)
// Si succès: fmt.Printf("Successfully purchased %d units of '%s'\n", quantity, itemName)
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool