Menu
Coddy logo textTech

Le polymorphisme via les interfaces

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 41 sur 107.

Le polymorphisme permet de traiter différents types de manière uniforme via une interface partagée. En Go, cela est réalisé entièrement via des interfaces, sans héritage ni hiérarchies de classes.

Lorsqu'une fonction accepte un type d'interface comme paramètre, n'importe quel type concret qui implémente cette interface peut lui être passé. La fonction n'a pas besoin de connaître le type spécifique — elle se soucie uniquement du comportement défini par l'interface :

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Maintenant MakeSound fonctionne avec n'importe quel type qui possède une méthode Speak() :

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Wouf !
    MakeSound(cat)  // Miaou !
}

Le même appel de fonction produit un comportement différent selon le type réel passé. C'est le polymorphisme en action. La fonction MakeSound est écrite une seule fois mais fonctionne avec un nombre illimité de types, tant qu'ils satisfont l'interface Speaker.

Cette approche rend votre code flexible et extensible. L'ajout d'un nouveau type qui parle ne nécessite aucune modification des fonctions existantes — il suffit d'implémenter l'interface, et cela fonctionne automatiquement.

challenge icon

Défi

Facile

Construisons un système de description de véhicules qui démontre le polymorphisme en action. Vous allez créer différents types de véhicules qui partagent tous un comportement commun via une interface, puis écrire une fonction unique qui fonctionne avec n'importe quel véhicule.

Vous organiserez votre code sur deux fichiers :

  • vehicles.go : Définissez une interface Describer qui requiert une méthode Describe() string. Ensuite, créez trois types de véhicules qui implémentent chacun cette interface à leur manière :
    • Car avec les champs Brand et Model—sa méthode Describe() retourne "Car: [Brand] [Model]"
    • Motorcycle avec les champs Brand et EngineCC (int)—sa méthode Describe() retourne "Motorcycle: [Brand] [EngineCC]cc"
    • Bicycle avec le champ Type (comme "Mountain" ou "Road")—sa méthode Describe() retourne "Bicycle: [Type]"
  • main.go : Créez une fonction appelée PrintDescription qui accepte n'importe quel Describer et affiche le résultat de l'appel à Describe(). Lisez les détails des véhicules depuis l'entrée standard, créez une instance de chaque type de véhicule, et passez chacune d'elles à PrintDescription pour démontrer que la même fonction fonctionne avec les trois types différents.

Les entrées suivantes seront fournies :

  • Ligne 1 : Marque de la voiture (Car brand)
  • Ligne 2 : Modèle de la voiture (Car model)
  • Ligne 3 : Marque de la moto (Motorcycle brand)
  • Ligne 4 : Cylindrée de la moto (Motorcycle engine CC - entier)
  • Ligne 5 : Type de vélo (Bicycle type)

Par exemple, avec Toyota, Camry, Honda, 600, et Mountain, votre sortie devrait être :

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Remarquez comment PrintDescription n'a pas besoin de savoir s'il reçoit une Car, une Motorcycle ou un Bicycle—il appelle simplement Describe() et chaque type répond avec sa propre sortie unique. C'est le polymorphisme : une fonction, plusieurs comportements.

Aide-mémoire

Le polymorphisme permet de traiter différents types de manière uniforme via une interface partagée. En Go, cela est réalisé grâce aux interfaces, sans héritage ni hiérarchies de classes.

Lorsqu'une fonction accepte un type d'interface comme paramètre, n'importe quel type concret qui implémente cette interface peut être passé :

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

La même fonction fonctionne avec différents types :

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Woof!
    MakeSound(cat)  // Meow!
}

La fonction est écrite une seule fois mais fonctionne avec une infinité de types, tant qu'ils satisfont l'interface. L'ajout de nouveaux types ne nécessite aucune modification des fonctions existantes — il suffit d'implémenter l'interface.

Essayez vous-même

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: Créer une fonction appelée PrintDescription qui accepte n'importe quel Describer
// et affiche le résultat de l'appel à Describe()

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Lire les détails de la voiture
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Lire les détails de la moto
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Lire les détails du vélo
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Créer un Car, un Motorcycle et un Bicycle en utilisant les valeurs d'entrée

	// TODO: Appeler PrintDescription pour chaque véhicule afin de démontrer le polymorphisme
	fmt.Println("TODO: Print vehicle descriptions")
}
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