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 de l’expression : « Si ça marche comme un canard et fait coin-coin 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 de disposer des bonnes méthodes.
Considérez cette interface et deux types totalement indépendants :
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, tous deux respectent 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{}) // Coin-coin !
MakeItQuack(Robot{}) // Bip-coin !
}
Cette satisfaction implicite est puissante, car elle vous permet de définir des interfaces après l’existence des types. Vous pouvez créer une interface correspondant à des types provenant de packages externes sans modifier leur code source. Les types n’ont pas besoin de connaître votre interface. Ils doivent simplement se comporter correctement.
Le typage canard en Go vous offre la flexibilité des langages dynamiques tout en maintenant la sécurité des types à la compilation. Le compilateur vérifie que les types possèdent effectivement les méthodes requises avant d’autoriser leur utilisation comme valeurs d’interface.
Défi
FacileConstruisons un système de messagerie qui démontre le duck typing en action. Tu vas 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.
Tu organiseras ton code sur trois fichiers :
messengers.go: crée trois structures indépendantes qui ont par hasard le même comportement :Phoneavec un champNumber. Sa méthodeSendMessage(text string) stringrenvoieSMS to [Number]: [text]Computeravec un champEmail. Sa méthodeSendMessage(text string) stringrenvoieEmail to [Email]: [text]Pigeonavec un champName. Sa méthodeSendMessage(text string) stringrenvoie[Name] carries: [text]
sender.go: définis une interfaceMessengerqui requiert une méthodeSendMessage(text string) string. Crée une fonction appeléeBroadcastqui prend une tranche deMessengeret une chaîne de caractères représentant un message, puis renvoie une tranche de chaînes contenant le résultat de l’appel deSendMessagesur chaque messager.main.go: lis les informations saisies, crée un exemplaire de chaque type de messager, rassemble-les dans une tranche deMessengeret utiliseBroadcastpour envoyer un message à chacun d’eux. Affiche 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, ta sortie devrait être :
SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello WorldL’idée essentielle 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, comme ils « >couinent » tous de la même manière (ils possèdent la méthode correspondante), Go permet de tous les utiliser comme valeurs Messenger. C’est le duck typing en action.
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()
// Read email address
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: Collecter tous les messagers dans une slice de Messenger
// TODO: Utiliser Broadcast pour envoyer le message via tous les messagers
// TODO: Afficher chaque résultat sur sa propre ligne
// Placeholder pour utiliser les variables (à supprimer lors de l'implémentation)
_ = phoneNumber
_ = email
_ = pigeonName
_ = message
}
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 et non exportésIntroduction à la POO en GoStructs comme classesDéfinir des méthodes sur des structsRécepteurs pointeurs ou par valeurInitialisation des structsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL’interface comme contratInterface vide (any)Assertion de typeCommutation de typeComposition d’interfacesInterfaces Stringer et ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter et SetterDissimulation de l’information en GoRécapitulatif – Fiches d’étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesSolution de contournement pour les méthodes génériquesRécapitulatif - Collection générique2Plongée approfondie dans les types et les structs
Types de base et compositesDéfinitions de types personnalisésTags de structStructs anonymesStructs imbriquésValeurs zéro et valeurs par défautRécapitulatif - Carnet de contacts5La composition plutôt que l’héritage
Pourquoi Go n’a pas d’héritageBases de l’inclusion de structsPromotion des méthodesInclure plusieurs structsInclusion ou agrégationMasquage des méthodes inclusesRécapitulatif : hiérarchie des employés8Gestion des erreurs et POO
L’interface errorTypes d’erreurs personnalisésEnrobage des erreurs (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - Analyseur de fichiers11Bibliothèque standard et POO
io.Reader et io.Writersort.InterfaceInterface fmt.Stringerencoding/json avec des structsInterface http.HandlerRécapitulatif - Modèles d’API REST14Modèles de conception – Partie 2
Modèle CommandeModèle AdaptateurModèle DécorateurModèle Méthode modèleModèle ÉtatModèle CompositeMiddleware comme décorateur3Pointeurs et mémoire
Notions de base des pointeurs en GoPointeurs vers des structuresPassage par valeur ou par référenceLa fonction new()Garbage collection en GoRécapitulatif - Constructeur de listes chaînées6Polymorphisme en Go
Polymorphisme via les interfacesDuck typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif – Processeur de paiements9Concurrence et POO
Bases des GoroutinesCanaux et communicationCanaux tamponnés ou non tamponnésInstruction selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures thread-safeRécapitulatif - Pool de workersEntraînez-vous par vous-même : Compilateur Go en ligne