Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 AudioPlayer avec un champ Brand (string) et une méthode Play() string qui retourne [Brand] playing audio
    • Une interface Playable exigeant une méthode Play() string
    • Une structure Smartphone avec un champ Model qui intègre AudioPlayer
    • Une structure Tablet avec un champ Model qui intègre AudioPlayer
  • main.go : Créez une fonction appelée StartPlayback qui accepte n'importe quel Playable et 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'interface Playable grâce à leur AudioPlayer intégré — même si aucun des deux types n'implémente explicitement Play() 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 audio

Remarquez 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")
}
quiz iconTestez-vous

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