Menu
Coddy logo textTech

Patron Composite

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

Le pattern Composite est un pattern structurel qui vous permet de composer des objets en structures arborescentes et de traiter uniformément les objets individuels et les groupes d’objets. Il est parfait pour représenter des hiérarchies partie-tout dans lesquelles vous souhaitez travailler à la fois avec des éléments uniques et des collections via la même interface.

Le modèle comporte trois composants clés : une interface de composant qui définit les opérations communes, des classes feuilles représentant des objets individuels et des classes composites qui contiennent des enfants et implémentent la même interface :

public interface IFileSystemItem
{
    string Name { get; }
    int GetSize();
}

public class File : IFileSystemItem
{
    public string Name { get; }
    private int _size;
    
    public File(string name, int size)
    {
        Name = name;
        _size = size;
    }
    
    public int GetSize() => _size;
}

public class Folder : IFileSystemItem
{
    public string Name { get; }
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();
    
    public Folder(string name) => Name = name;
    
    public void Add(IFileSystemItem item) => _items.Add(item);
    
    public int GetSize() => _items.Sum(item => item.GetSize());
}

La beauté de ce patron est que les clients n'ont pas besoin de savoir s'ils travaillent avec un seul fichier ou une hiérarchie complète de dossiers :

var root = new Folder("root");
root.Add(new File("doc.txt", 100));

var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));

root.Add(subFolder);

Console.WriteLine(root.GetSize());  // 650

Le modèle Composite est idéal pour les systèmes de fichiers, les arbres de composants d’interface utilisateur, les organigrammes ou toute structure où les conteneurs et leur contenu doivent être traités de la même manière. Il simplifie le code client en éliminant la nécessité de distinguer les éléments simples des éléments complexes.

challenge icon

Défi

Facile

Construisons un système d’organigramme en utilisant le pattern Composite. Vous allez créer une structure dans laquelle les employés individuels et les services (qui contiennent des employés ou d’autres services) peuvent être traités uniformément, ce qui vous permettra de calculer le coût salarial total à n’importe quel niveau de la hiérarchie.

Vous organiserez votre code sur trois fichiers :

  • OrgComponent.cs : définissez une interface IOrgComponent dans l’espace de noms Organization avec deux membres : une propriété Name (string) et une méthode GetSalary() qui renvoie un entier. Cette interface représente le contrat commun que respecteront les employés individuels et les services.
  • OrgClasses.cs : créez deux classes dans le même espace de noms :
    • Employee — une classe feuille représentant un employé individuel. Elle reçoit un nom et un salaire dans son constructeur et implémente l’interface en renvoyant son propre salaire.
    • Department — une classe composite représentant un groupe. Elle reçoit un nom dans son constructeur et conserve une liste d’éléments IOrgComponent. Incluez une méthode Add(IOrgComponent component) pour ajouter des membres. Sa méthode GetSalary() doit renvoyer la somme des salaires de tous ses membres.
  • Program.cs : construisez une structure organisationnelle à partir des entrées et calculez le salaire total. Vous créerez des services et des employés, les imbriquerez de manière appropriée et montrerez comment le pattern Composite vous permet de traiter uniformément l’ensemble de la hiérarchie.

Vous recevrez les entrées suivantes :

  • Le nombre de composants à créer
  • Pour chaque composant : le type (employee ou department), suivi du nom et, pour les employés, de leur salaire. Pour les services, vous recevrez également le nom du service parent (ou root s’il s’agit d’un service de niveau supérieur)

Format d’entrée pour chaque composant :

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Après avoir construit la structure, affichez le salaire total du service racine au format {root_name} Total Salary: {amount}.

Par exemple, si les entrées sont :

5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QA

La sortie devrait être :

Engineering Total Salary: 13500

Le service Engineering contient Alice (5000), Bob (4500) et le service QA. QA contient Charlie (4000). Lorsque vous appelez GetSalary() sur Engineering, il calcule récursivement le total de tous les composants imbriqués — ce qui démontre comment le pattern Composite vous permet de travailler avec des structures arborescentes complexes au moyen d’une interface simple et uniforme !

Essayez vous-même

using System;
using System.Collections.Generic;
using Organization;

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO : Créer un dictionnaire pour stocker les départements par nom
        // Cela vous aidera à trouver les départements parents lors de l'ajout de composants
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO : Garder une trace du département racine
        Department root = null;
        
        for (int i = 0; i < n; i++)
        {
            string type = Console.ReadLine();
            
            if (type == "department")
            {
                string name = Console.ReadLine();
                string parent = Console.ReadLine();
                
                // TODO : Créer le département
                // TODO : L'ajouter au dictionnaire departments
                // TODO : Si parent est "root", c'est le département racine
                // TODO : Sinon, trouver le département parent et lui ajouter ce département
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO : Créer l'employé
                // TODO : Trouver le département parent et lui ajouter l'employé
            }
        }
        
        // TODO : Afficher le salaire total du département racine
        // Format : "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
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 C# en ligne