Masquage de méthodes imbriquées
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 39 sur 107.
Parfois, vous avez besoin que la structure externe fournisse sa propre implémentation d'une méthode que le type incorporé possède déjà. C'est ce qu'on appelle le shadowing, et cela vous permet de redéfinir les méthodes promues avec un comportement personnalisé.
Lorsque vous définissez une méthode sur la structure externe avec le même nom qu'une méthode intégrée, la méthode externe est prioritaire :
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
Maintenant, lorsque vous appelez Speak() sur un Dog, la méthode externe est utilisée :
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - méthode de Dog
fmt.Println(d.Animal.Speak()) // Some sound - méthode de Animal
}
La méthode incorporée n'a pas disparu. Vous pouvez toujours y accéder explicitement via le nom du type incorporé. Ce modèle est utile lorsque vous souhaitez personnaliser le comportement tout en conservant l'implémentation originale disponible, peut-être pour l'appeler depuis votre méthode de substitution :
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Le masquage (shadowing) vous donne le contrôle sur le comportement exposé tout en conservant l'accès à l'original par une qualification explicite.
Défi
FacileConstruisons un système de notification où différents canaux de notification peuvent personnaliser la manière dont ils formatent et délivrent les messages. Vous utiliserez le masquage de méthode (method shadowing) pour permettre à des canaux spécifiques de surcharger le comportement par défaut tout en pouvant toujours accéder à l'implémentation originale si nécessaire.
Vous organiserez votre code sur trois fichiers :
base.go: Créez une structureBaseNotificationavec les champsTitleetPriority(tous deux des chaînes de caractères). Donnez-lui une méthodeFormat() stringqui retourne la notification dans un format standard :[Priority] Title.channels.go: Créez deux types de canaux de notification qui intègrentBaseNotificationet masquent la méthodeFormatavec leurs propres implémentations :EmailNotificationavec un champRecipient— sa méthodeFormat()doit retournerEmail to [Recipient]: [Priority] TitleSlackNotificationavec un champChannel— sa méthodeFormat()doit appeler la méthodeBaseNotification.Format()intégrée et ajouter#[Channel]:au début du résultat
main.go: Lisez les détails de la notification depuis l'entrée standard, créez les deux types de notifications, et démontrez le masquage en affichant :- La sortie formatée de
EmailNotification(en utilisant sa méthode masquée) - Le format de base de
EmailNotification(en appelant explicitement la méthode intégrée) - La sortie formatée de
SlackNotification(qui utilise la méthode de base en interne)
- La sortie formatée de
Les entrées suivantes seront fournies :
- Ligne 1 : Titre de la notification
- Ligne 2 : Niveau de priorité
- Ligne 3 : Destinataire de l'e-mail
- Ligne 4 : Nom du canal Slack
Par exemple, avec Server Alert, HIGH, admin@company.com, et ops-alerts, votre sortie devrait être :
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertRemarquez comment EmailNotification remplace complètement le formatage de base, tandis que SlackNotification l'améliore en appelant la méthode originale et en ajoutant le contexte du canal. Les deux approches démontrent différentes manières d'utiliser efficacement le masquage de méthode.
Aide-mémoire
Lorsque vous définissez une méthode sur une structure externe avec le même nom qu'une méthode d'un type incorporé, la méthode externe masque (surcharge) la méthode incorporée :
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
Lors de l'appel de la méthode sur la structure externe, la méthode externe est prioritaire :
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof! - méthode de Dog
fmt.Println(d.Animal.Speak()) // Some sound - méthode de Animal
La méthode incorporée reste accessible via une qualification explicite en utilisant le nom du type incorporé. Vous pouvez appeler l'implémentation d'origine à l'intérieur de la méthode de surcharge :
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Lire le titre de la notification
scanner.Scan()
title := scanner.Text()
// Lire le niveau de priorité
scanner.Scan()
priority := scanner.Text()
// Lire le destinataire de l'e-mail
scanner.Scan()
recipient := scanner.Text()
// Lire le nom du canal Slack
scanner.Scan()
channel := scanner.Text()
// TODO: Créer une EmailNotification avec la notification de base, le titre, la priorité et le destinataire
// TODO: Créer une SlackNotification avec la notification de base, le titre, la priorité et le canal
// TODO: Afficher la sortie formatée de EmailNotification (en utilisant sa méthode masquée)
// TODO: Afficher le format de base de EmailNotification (en appelant explicitement la méthode intégrée)
// TODO: Afficher la sortie formatée de SlackNotification
fmt.Println() // Espace réservé - à supprimer lors de l'implémentation
}
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