Promotion de méthodes
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 36 sur 107.
Tout comme l'imbrication de structures (struct embedding) promeut les champs vers la structure externe, Go promeut également les méthodes des types imbriqués. Cela signifie que vous pouvez appeler des méthodes définies sur la structure imbriquée directement sur la structure externe.
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // intégré
}
Puisque Car imbrique Engine, la méthode Start() est automatiquement promue :
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Moteur démarré
fmt.Println(c.Engine.Start()) // fonctionne également
}
Cette promotion a une conséquence importante pour les interfaces. Si le type imbriqué satisfait une interface, le type externe la satisfait également automatiquement :
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car satisfait Starter via Engine
}
Le type Car n'implémente jamais explicitement Starter, pourtant il satisfait l'interface parce que son Engine incorporé possède la méthode requise. C'est ainsi que Go parvient à la réutilisation de comportement sans l'héritage traditionnel.
Défi
FacileConstruisons un système de lecteur de musique qui démontre comment les méthodes des types intégrés sont promues vers le type extérieur, et comment cela permet de satisfaire une interface par composition.
Vous organiserez votre code sur deux fichiers :
audio.go: Créez les blocs de construction pour votre lecteur de musique :- Une structure
AudioPlayeravec un champBrand(string) et une méthodePlay() stringqui retourne[Brand] playing audio - Une interface
Playableexigeant une méthodePlay() string - Une structure
Smartphoneavec un champModelqui intègreAudioPlayer - Une structure
Tabletavec un champModelqui intègreAudioPlayer
- Une structure
main.go: Créez une fonction appeléeStartPlaybackqui accepte n'importe quelPlayableet retourne le résultat de l'appel àPlay(). Lisez les informations de l'appareil à partir de l'entrée, créez à la fois un Smartphone et une Tablette, et démontrez que les deux satisfont l'interfacePlayablegrâce à leurAudioPlayerintégré — même si aucun des deux types n'implémente explicitementPlay()lui-même.
Les entrées suivantes seront fournies :
- Ligne 1 : Nom du modèle du Smartphone
- Ligne 2 : Marque audio du Smartphone
- Ligne 3 : Nom du modèle de la Tablette
- Ligne 4 : Marque audio de la Tablette
Pour chaque appareil, affichez son modèle suivi du résultat de son passage à StartPlayback.
Par exemple, avec iPhone 15, Apple Audio, iPad Pro, et Beats, votre sortie devrait être :
iPhone 15
Apple Audio playing audio
iPad Pro
Beats playing audioRemarquez comment Smartphone et Tablet peuvent tous deux être passés à StartPlayback car la méthode Play() de AudioPlayer est automatiquement promue vers chaque type extérieur, satisfaisant l'interface Playable sans aucun code supplémentaire.
Aide-mémoire
Lorsqu'une structure intègre un autre type, les méthodes du type intégré sont automatiquement promues à la structure externe :
type Engine struct {
Horsepower int
}
func (e Engine) Start() string {
return "Engine started"
}
type Car struct {
Model string
Engine // embedded
}
func main() {
c := Car{
Model: "Sedan",
Engine: Engine{Horsepower: 200},
}
fmt.Println(c.Start()) // Engine started
fmt.Println(c.Engine.Start()) // also works
}
Si un type intégré satisfait une interface, le type externe la satisfait également automatiquement :
type Starter interface {
Start() string
}
func Ignite(s Starter) {
fmt.Println(s.Start())
}
func main() {
c := Car{Model: "Sedan", Engine: Engine{Horsepower: 200}}
Ignite(c) // Car satisfies Starter through Engine
}
Cela permet la réutilisation du comportement par composition sans l'héritage traditionnel.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
)
// TODO: Créer une fonction StartPlayback qui accepte n'importe quel Playable
// et retourne le résultat de l'appel à Play()
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Lire le modèle du smartphone
scanner.Scan()
smartphoneModel := scanner.Text()
// Lire la marque audio du smartphone
scanner.Scan()
smartphoneBrand := scanner.Text()
// Lire le modèle de la tablette
scanner.Scan()
tabletModel := scanner.Text()
// Lire la marque audio de la tablette
scanner.Scan()
tabletBrand := scanner.Text()
// TODO: Créer un Smartphone avec l'AudioPlayer intégré
// TODO: Créer une Tablette avec l'AudioPlayer intégré
// TODO: Afficher le modèle du smartphone, puis appeler StartPlayback avec le smartphone
// TODO: Afficher le modèle de la tablette, puis appeler StartPlayback avec la tablette
_ = smartphoneModel
_ = smartphoneBrand
_ = tabletModel
_ = tabletBrand
fmt.Println("TODO: Complete the implementation")
}
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