Menu
Coddy logo textTech

Collections polymorphes

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

L'une des applications les plus puissantes des interfaces est le stockage de différents types dans la même collection. Une slice d'un type interface peut contenir n'importe quelle valeur qui satisfait cette interface, vous permettant de regrouper des types liés mais différents.

Considérez un scénario dans lequel vous devez gérer différentes formes. Au lieu d'utiliser des slices distinctes pour chaque type, vous pouvez utiliser une seule slice du type interface :

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

La slice shapes contient à la fois des cercles et des rectangles. Lors de l'itération, chaque élément répond à Area() selon son type réel. Il s'agit du polymorphisme appliqué aux collections — la même boucle gère tous les types de formes de manière uniforme.

Les collections polymorphes sont essentielles lors de la construction de systèmes qui traitent des éléments variés : un système de notification envoyant des e-mails et des SMS, un jeu mettant à jour différents types d'ennemis, ou un processeur de documents gérant plusieurs formats de fichiers. La collection ne se soucie pas des types spécifiques — seulement que chaque élément fournisse le comportement requis.

challenge icon

Défi

Facile

Construisons un système de gestion de tâches qui gère différents types de tâches via une interface unifiée. Vous allez créer divers types de tâches et les traiter tous ensemble dans une collection polymorphe.

Vous organiserez votre code sur trois fichiers :

  • task.go : Définissez une interface Task qui requiert une méthode Summary() string. Cette interface sera le contrat commun que tous les types de tâches doivent remplir.
  • types.go : Créez trois types de tâches différents qui implémentent chacun l'interface Task :
    • BugFix avec les champs ID (string) et Severity (string)—sa méthode Summary() retourne Bug #[ID] ([Severity])
    • Feature avec les champs Name (string) et Points (int)—sa méthode Summary() retourne Feature: [Name] - [Points] pts
    • Documentation avec le champ Topic (string)—sa méthode Summary() retourne Docs: [Topic]
  • main.go : Créez une fonction nommée PrintBacklog qui accepte une slice de Task et affiche le résumé de chaque tâche sur sa propre ligne. Lisez les détails des tâches depuis l'entrée standard, créez une instance de chaque type de tâche, rassemblez-les toutes dans une seule slice []Task, et passez-la à PrintBacklog.

Les entrées suivantes seront fournies :

  • Ligne 1 : Bug ID
  • Ligne 2 : Bug severity
  • Ligne 3 : Feature name
  • Ligne 4 : Feature points (entier)
  • Ligne 5 : Documentation topic

Par exemple, avec 1042, critical, Dark Mode, 8, et API Reference, votre sortie devrait être :

Bug #1042 (critical)
Feature: Dark Mode - 8 pts
Docs: API Reference

La puissance ici réside dans le fait que PrintBacklog n'a pas besoin d'une logique distincte pour les bugs, les fonctionnalités ou la documentation—elle itère simplement à travers la slice et appelle Summary() sur chaque élément. Chaque type de tâche répond avec son propre format unique, démontrant comment les collections polymorphes vous permettent de traiter des types divers de manière uniforme.

Aide-mémoire

Une tranche (slice) d'un type interface peut contenir n'importe quelle valeur qui satisfait cette interface, permettant ainsi des collections polymorphes :

type Shape interface {
    Area() float64
}

type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }

type Rectangle struct{ Width, Height float64 }
func (r Rectangle) Area() float64 { return r.Width * r.Height }

func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 3},
        Circle{Radius: 2},
    }
    
    for _, s := range shapes {
        fmt.Printf("Area: %.2f\n", s.Area())
    }
}

La tranche shapes contient différents types (Circle et Rectangle) qui implémentent tous l'interface Shape. Lors de l'itération, chaque élément répond à Area() selon son type réel — c'est le polymorphisme appliqué aux collections.

Ce modèle est utile pour traiter uniformément des éléments variés : des systèmes de notification gérant différents types de messages, des jeux mettant à jour divers types d'ennemis, ou des processeurs de documents gérant plusieurs formats de fichiers.

Essayez vous-même

package main

import (
	"fmt"
)

// TODO: Créer une fonction PrintBacklog qui accepte une slice de Task
// et affiche le résumé de chaque tâche sur sa propre ligne

func main() {
	// Lire l'entrée
	var bugID string
	var bugSeverity string
	var featureName string
	var featurePoints int
	var docTopic string

	fmt.Scanln(&bugID)
	fmt.Scanln(&bugSeverity)
	fmt.Scanln(&featureName)
	fmt.Scanln(&featurePoints)
	fmt.Scanln(&docTopic)

	// TODO: Créer un exemplaire de chaque type de tâche (BugFix, Feature, Documentation)

	// TODO: Collecter toutes les tâches dans une seule slice []Task

	// TODO: Appeler PrintBacklog avec la slice de tâches
}
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