Menu
Coddy logo textTech

Implémentation implicite

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

Dans de nombreux langages orientés objet comme Java ou C#, vous devez déclarer explicitement qu'un type implémente une interface en utilisant des mots-clés comme implements. Go adopte une approche différente : l'implémentation d'une interface est implicite.

Un type satisfait une interface simplement en implémentant toutes ses méthodes. Il n'est pas nécessaire de déclarer cette relation explicitement. Le compilateur le détermine automatiquement.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implémente implicitement Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Remarquez qu'il n'y a pas de déclaration implements Writer sur FileWriter. Parce que FileWriter possède une méthode Write(data string) int correspondant à la signature de l'interface, elle satisfait automatiquement l'interface Writer.

Cette conception a une conséquence puissante : vous pouvez définir des interfaces après que les types existent déjà. Si une bibliothèque tierce possède un type avec les méthodes dont vous avez besoin, vous pouvez créer une interface qu'il satisfait déjà sans modifier le code original.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Fonctionne car FileWriter satisfait Writer
}

L'implémentation implicite permet de garder le code Go découplé et flexible. Les types n'ont pas besoin de connaître les interfaces à l'avance, ce qui facilite la composition de systèmes à partir de parties indépendantes.

challenge icon

Défi

Facile

Construisons un système de distribution de messages qui démontre comment les types satisfont implicitement les interfaces en Go. Vous allez créer différents types de messagers qui satisfont tous la même interface, sans jamais déclarer explicitement cette relation.

Vous organiserez votre code sur deux fichiers :

  • messengers.go : Définissez une interface Messenger avec une seule méthode Send(message string) string. Ensuite, créez deux structs qui satisferont implicitement cette interface :
    • EmailMessenger avec un champ Address (string)
    • SMSMessenger avec un champ PhoneNumber (string)
    Chaque struct doit avoir une méthode Send qui retourne une chaîne de caractères décrivant l'envoi. Aucune des deux structs ne doit déclarer explicitement qu'elle implémente Messenger — Go le déterminera automatiquement en fonction des signatures de méthode.
  • main.go : Créez une fonction appelée DeliverMessage qui accepte n'importe quel Messenger et une chaîne de caractères de message, puis retourne le résultat de l'appel à Send. Lisez les informations de contact à partir de l'entrée, créez les deux types de messagers et utilisez votre fonction pour distribuer les messages via chacun d'eux.

Les entrées suivantes seront fournies :

  • Ligne 1 : Adresse e-mail
  • Ligne 2 : Numéro de téléphone
  • Ligne 3 : Message à envoyer

Vos méthodes Send doivent retourner des chaînes de caractères dans ces formats :

  • EmailMessenger : Email to [Address]: [message]
  • SMSMessenger : SMS to [PhoneNumber]: [message]

Par exemple, avec alice@example.com, 555-1234, et Hello!, votre sortie devrait être :

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

L'idée clé ici est que votre fonction DeliverMessage accepte n'importe quel Messenger, et que EmailMessenger tout comme SMSMessenger satisfont cette interface simplement en ayant une méthode Send correspondante. Il n'y a aucun mot-clé implements nulle part — l'implémentation implicite de Go gère tout.

Aide-mémoire

En Go, l'implémentation d'une interface est implicite. Un type satisfait une interface simplement en implémentant toutes ses méthodes — aucune déclaration explicite n'est nécessaire.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter implémente implicitement Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Il n'y a pas de déclaration implements Writer. Parce que FileWriter possède une méthode Write(data string) int correspondant à la signature de l'interface, il satisfait automatiquement l'interface Writer.

Vous pouvez utiliser n'importe quel type qui satisfait une interface sans que le type ne connaisse l'interface :

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Fonctionne car FileWriter satisfait Writer
}

Cette conception vous permet de définir des interfaces après que les types existent déjà, rendant le code découplé et flexible.

Essayez vous-même

package main

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

// TODO: Créer une fonction DeliverMessage qui accepte n'importe quel Messenger
// et une chaîne de caractères message, puis retourne le résultat de l'appel à Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Lire l'adresse e-mail
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Lire le numéro de téléphone
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Lire le message
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Créer un EmailMessenger avec l'adresse e-mail
	
	// TODO: Créer un SMSMessenger avec le numéro de téléphone
	
	// TODO: Utiliser DeliverMessage pour envoyer le message via chaque messager
	// et afficher les résultats
}
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