Menu
Coddy logo textTech

Composition plutôt qu'héritage

Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 41 sur 70.

L'héritage crée un couplage fort entre les classes. Lorsqu'une Car hérite de Vehicle, toute modification apportée à Vehicle peut casser Car. La composition offre une alternative plus flexible : au lieu d'hériter d'un comportement, une classe contient des objets qui fournissent ce comportement.

Considérez un personnage de jeu qui peut se déplacer et attaquer. Avec l'héritage, vous pourriez créer une hiérarchie complexe. Avec la composition, vous donnez simplement au personnage les composants dont il a besoin :

public class MovementBehavior
{
    public void Move() => Console.WriteLine("Moving...");
}

public class AttackBehavior
{
    public void Attack() => Console.WriteLine("Attacking!");
}

public class Character
{
    private MovementBehavior movement = new MovementBehavior();
    private AttackBehavior attack = new AttackBehavior();
    
    public void Move() => movement.Move();
    public void Attack() => attack.Attack();
}

La différence clé est la relation. L'héritage représente « est un » (un Chien est un Animal), tandis que la composition représente « a un » (un Personnage a un comportement de mouvement). La composition vous permet d'échanger des comportements au moment de l'exécution, de combiner librement des capacités et d'éviter le problème de la classe de base fragile.

Utilisez l'héritage lorsqu'il existe une véritable relation hiérarchique. Choisissez la composition lorsque vous avez besoin de flexibilité, lorsque des comportements peuvent être partagés entre des classes non liées, ou lorsque vous souhaitez modifier le comportement de manière dynamique.

challenge icon

Défi

Facile

Construisons un système de notification qui démontre la puissance de la composition par rapport à l'héritage. Au lieu de créer une hiérarchie complexe de classes de notification, vous allez composer un NotificationSender en lui donnant des composants de comportement interchangeables.

Vous organiserez votre code sur trois fichiers :

  • Behaviors.cs : Créez deux classes de comportement dans l'espace de noms Notifications qui gèrent différents aspects de l'envoi de notifications :
    • FormattingBehavior - possède une méthode Format(string message) qui renvoie le message enveloppé avec [ALERT] au début et [END] à la fin
    • DeliveryBehavior - possède une méthode Deliver(string channel, string message) qui renvoie une chaîne au format Sending via {channel}: {message}
  • NotificationSender.cs : Créez une classe NotificationSender dans l'espace de noms Notifications qui utilise la composition pour combiner les comportements. Votre expéditeur doit :
    • Contenir des instances de FormattingBehavior et DeliveryBehavior en tant que champs privés
    • Avoir une propriété Channel (string) qui stocke le nom du canal de distribution
    • Avoir un constructeur qui accepte le nom du canal
    • Avoir une méthode Send(string message) qui formate d'abord le message à l'aide du comportement de formatage, puis le livre à l'aide du comportement de livraison, et renvoie le résultat final
  • Program.cs : Dans votre fichier principal, créez un NotificationSender avec un canal provenant de l'entrée, puis envoyez un message provenant de l'entrée. Affichez le résultat pour montrer comment les comportements composés fonctionnent ensemble.

Vous recevrez deux entrées :

  • Le nom du canal de livraison (par exemple, Email, SMS)
  • Le message à envoyer

Affichez le résultat de l'appel à Send() sur votre expéditeur de notification.

Par exemple, si les entrées sont Email et Server is down, la sortie doit être :

Sending via Email: [ALERT] Server is down [END]

Remarquez comment le NotificationSender n'hérite de rien - il "possède" simplement des comportements de formatage et de livraison. Cette approche par composition signifie que vous pourriez facilement échanger l'un ou l'autre comportement sans modifier la classe de l'expéditeur, ou réutiliser ces comportements dans des classes complètement différentes !

Aide-mémoire

La composition est une alternative à l'héritage qui crée un code plus flexible en faisant en sorte qu'une classe contienne des objets qui fournissent un comportement, plutôt que d'hériter du comportement d'une classe parente.

Héritage vs Composition :

  • L'héritage représente des relations de type « est un » (un Chien est un Animal)
  • La composition représente des relations de type « a un » (un Personnage a un comportement de mouvement)

Avantages de la composition :

  • Changer les comportements au moment de l'exécution
  • Combiner les capacités librement
  • Éviter le couplage fort et les problèmes de classe de base fragile
  • Partager des comportements entre des classes non liées

Exemple avec des composants de comportement :

public class MovementBehavior
{
    public void Move() => Console.WriteLine("Moving...");
}

public class AttackBehavior
{
    public void Attack() => Console.WriteLine("Attacking!");
}

public class Character
{
    private MovementBehavior movement = new MovementBehavior();
    private AttackBehavior attack = new AttackBehavior();
    
    public void Move() => movement.Move();
    public void Attack() => attack.Attack();
}

Quand utiliser chaque approche :

  • Utilisez l'héritage pour les relations hiérarchiques réelles
  • Choisissez la composition pour la flexibilité, les comportements partagés ou les changements de comportement dynamiques

Essayez vous-même

using System;
using Notifications;

class Program
{
    public static void Main(string[] args)
    {
        // Lire l'entrée
        string channel = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: Créer un NotificationSender avec le canal
        
        // TODO: Envoyer le message et afficher le résultat
        
    }
}
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