Menu
Coddy logo textTech

Pourquoi Go n’a pas d’héritage

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 34 sur 107.

Les langages traditionnels orientés objet comme Java et C++ utilisent l’héritage pour partager du code entre les types. Une classe enfant étend une classe parente et hérite de tous ses champs et méthodes. Go omet délibérément cette fonctionnalité.

L’héritage crée un couplage étroit entre les types. Lorsqu’une classe parente change, toutes les classes enfants sont affectées.

Les hiérarchies d’héritage profondes deviennent difficiles à comprendre et à maintenir. Le « problème de la classe de base fragile » survient lorsque des modifications apportées à une classe de base interrompent de manière inattendue les classes dérivées.

Les concepteurs de Go ont choisi une voie différente : la composition plutôt que l'héritage. Au lieu de dire « un Dog est un Animal », Go vous encourage à dire « un Dog has des comportements semblables à ceux d'un Animal ». Ce changement subtil mène à un code plus flexible et plus facile à maintenir.

Go réalise la réutilisation du code grâce à deux mécanismes que vous avez déjà appris :

  • Interfaces définissent des contrats de comportement sans détails d’implémentation
  • L’intégration de structures permet aux types d’inclure d’autres types et de réutiliser leurs méthodes

Considérez cette comparaison. En POO traditionnelle, vous pourriez écrire class Dog extends Animal. En Go, vous intégrez une structure Animal dans Dog et implémentez des interfaces partagées. Le résultat offre des fonctionnalités similaires avec un couplage plus faible entre les types.

Ce chapitre explore en profondeur l’intégration de structures, en montrant comment Go obtient les avantages de l’héritage sans ses inconvénients.

challenge icon

Défi

Facile

Construisons un système de notifications qui illustre l'approche de composition de Go plutôt que l'héritage. Vous allez créer des types qui partagent un comportement grâce aux interfaces et à l'intégration de structures, plutôt qu'à des hiérarchies de classes.

Vous organiserez votre code sur trois fichiers :

  • notifier.go : définissez une interface Notifier avec une seule méthode Notify(message string) string. Créez également une structure BaseNotifier avec un champ Name qui sera intégrée par les autres types. Donnez à BaseNotifier une méthode appelée Format(message string) string qui renvoie le message précédé du nom du notificateur entre crochets.
  • channels.go : créez deux types de canaux de notification qui intègrent BaseNotifier et implémentent l'interface Notifier :
    • EmailNotifier avec un champ supplémentaire Address
    • SMSNotifier avec un champ supplémentaire Phone
    La méthode Notify de chaque type doit utiliser la méthode Format intégrée et inclure les informations spécifiques à son canal dans le résultat.
  • main.go : créez une fonction appelée SendAlert qui accepte n'importe quel Notifier ainsi qu'un message, puis renvoie le résultat de l'appel à Notify. Lisez les détails des notifications depuis l'entrée, créez les deux types de notificateurs et montrez comment ils peuvent être utilisés de manière interchangeable grâce à l'interface.

Les entrées suivantes seront fournies :

  • Ligne 1 : nom du notificateur pour l'e-mail
  • Ligne 2 : adresse e-mail
  • Ligne 3 : nom du notificateur pour le SMS
  • Ligne 4 : numéro de téléphone
  • Ligne 5 : message d'alerte

Votre méthode Format sur BaseNotifier doit renvoyer :

[Name] message

Vos méthodes Notify doivent renvoyer :

  • EmailNotifier : Email to [Address]: [formatted message]
  • SMSNotifier : SMS to [Phone]: [formatted message]

Par exemple, avec Alerts, user@mail.com, Urgent, 555-1234 et Server down, votre sortie doit être :

Email to user@mail.com: [Alerts] Server down
SMS to 555-1234: [Urgent] Server down

Remarquez comment les deux types de notificateurs réutilisent la méthode Format de BaseNotifier grâce à l'intégration, tandis que chacun fournit sa propre implémentation de Notify. La fonction SendAlert fonctionne avec n'importe quel Notifier sans connaître le type concret. C'est la composition plutôt que l'héritage en action.

Essayez vous-même

package main

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

// SendAlert accepte n'importe quel Notifier et un message, renvoie le résultat de l'appel à Notify
// TODO: Implémenter la fonction SendAlert

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Lire le nom du notificateur e-mail
	scanner.Scan()
	emailName := scanner.Text()
	
	// Lire l'adresse e-mail
	scanner.Scan()
	emailAddress := scanner.Text()
	
	// Lire le nom du notificateur SMS
	scanner.Scan()
	smsName := scanner.Text()
	
	// Lire le numéro de téléphone
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Lire le message d'alerte
	scanner.Scan()
	alertMessage := scanner.Text()
	
	// TODO: Créer un EmailNotifier avec emailName et emailAddress
	
	// TODO: Créer un SMSNotifier avec smsName et phoneNumber
	
	// TODO: Utiliser SendAlert pour envoyer le alertMessage via les deux notificateurs
	// et afficher les résultats
	
	// Supprimer les avertissements de variables non utilisées (retirez-les lorsque vous utilisez les variables)
	_ = emailName
	_ = emailAddress
	_ = smsName
	_ = phoneNumber
	_ = alertMessage
}
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

Entraînez-vous par vous-même : Compilateur Go en ligne