Menu
Coddy logo textTech

Le Duck Typing en Go

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

Le terme duck typing vient du dicton : « Si cela marche comme un canard et cancane comme un canard, alors c'est un canard. » En Go, cela signifie qu'un type n'a pas besoin de déclarer explicitement qu'il implémente une interface — il lui suffit d'avoir les bonnes méthodes.

Considérez cette interface et deux types complètement distincts :

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Ni Duck ni Robot ne mentionnent Quacker nulle part dans leurs définitions. Pourtant, les deux satisfont l'interface simplement parce qu'ils possèdent une méthode Quack() avec la signature correcte :

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Cette satisfaction implicite est puissante car elle vous permet de définir des interfaces après que les types existent déjà. Vous pouvez créer une interface qui correspond à des types provenant de packages externes sans modifier leur code source. Les types n'ont pas besoin de connaître votre interface — ils ont simplement besoin de se comporter correctement.

Le duck typing en Go vous offre la flexibilité des langages dynamiques tout en maintenant la sécurité du typage à la compilation. Le compilateur vérifie que les types possèdent réellement les méthodes requises avant de permettre leur utilisation en tant que valeurs d'interface.

challenge icon

Défi

Facile

Construisons un système de messagerie qui démontre le duck typing en action. Vous allez créer des types complètement indépendants qui peuvent tous envoyer des messages via une interface commune — sans qu'aucun d'entre eux ne déclare explicitement l'implémenter.

Vous organiserez votre code sur trois fichiers :

  • messengers.go : Créez trois structures indépendantes qui partagent par hasard le même comportement :
    • Phone avec un champ Number — sa méthode SendMessage(text string) string retourne "SMS to [Number]: [text]"
    • Computer avec un champ Email — sa méthode SendMessage(text string) string retourne "Email to [Email]: [text]"
    • Pigeon avec un champ Name — sa méthode SendMessage(text string) string retourne "[Name] carries: [text]"
    Remarquez qu'aucun de ces types ne mentionne d'interface — ils se trouvent simplement avoir la même signature de méthode.
  • sender.go : Définissez une interface Messenger qui requiert une méthode SendMessage(text string) string. Créez une fonction nommée Broadcast qui prend une tranche (slice) de Messenger et une chaîne de caractères pour le message, puis retourne une tranche de chaînes contenant le résultat de l'appel à SendMessage sur chaque messager.
  • main.go : Lisez les détails depuis l'entrée standard, créez un exemplaire de chaque type de messager, rassemblez-les dans une tranche de Messenger, et utilisez Broadcast pour envoyer un message à travers chacun d'eux. Affichez chaque résultat sur sa propre ligne.

Les entrées suivantes seront fournies :

  • Ligne 1 : Numéro de téléphone
  • Ligne 2 : Adresse e-mail
  • Ligne 3 : Nom du pigeon
  • Ligne 4 : Message à diffuser

Par exemple, avec "555-1234", "alice@mail.com", "Winston", et "Hello World", votre sortie devrait être :

SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello World

L'idée clé ici est que Phone, Computer, et Pigeon n'ont rien en commun — ils ne partagent pas de type de base et n'implémentent pas explicitement Messenger. Pourtant, parce qu'ils "cancanent" tous de la même manière (ils possèdent la méthode correspondante), Go permet de les utiliser tous comme des valeurs de type Messenger. C'est le duck typing à l'œuvre.

Aide-mémoire

En Go, le duck typing signifie qu'un type n'a pas besoin de déclarer explicitement qu'il implémente une interface — il lui suffit de posséder les méthodes requises avec des signatures correspondantes.

Un type satisfait implicitement une interface s'il possède toutes les méthodes requises par l'interface :

type Quacker interface {
    Quack() string
}

type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }

type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }

Duck et Robot satisfont tous deux l'interface Quacker sans le mentionner dans leurs définitions :

func MakeItQuack(q Quacker) {
    fmt.Println(q.Quack())
}

func main() {
    MakeItQuack(Duck{})   // Quack!
    MakeItQuack(Robot{})  // Beep-quack!
}

Cette satisfaction implicite vous permet de définir des interfaces après que les types existent déjà, et même de créer des interfaces qui correspondent à des types de packages externes sans modifier leur code source. Le compilateur vérifie au moment de la compilation que les types possèdent les méthodes requises, offrant ainsi une sécurité de typage tout en conservant de la souplesse.

Essayez vous-même

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Lire le numéro de téléphone
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Lire l'adresse e-mail
	scanner.Scan()
	email := scanner.Text()
	
	// Lire le nom du pigeon
	scanner.Scan()
	pigeonName := scanner.Text()
	
	// Lire le message à diffuser
	scanner.Scan()
	message := scanner.Text()
	
	// TODO : Créer un de chaque type de messager (Phone, Computer, Pigeon)
	
	// TODO : Rassembler tous les messagers dans une tranche (slice) de Messenger
	
	// TODO : Utiliser Broadcast pour envoyer le message via tous les messagers
	
	// TODO : Imprimer chaque résultat sur sa propre ligne
	
	// Espace réservé pour utiliser les variables (à supprimer lors de l'implémentation)
	_ = phoneNumber
	_ = email
	_ = pigeonName
	_ = message
}
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