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()); // 650Le 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.
Défi
FacileConstruisons 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 interfaceIOrgComponentdans l’espace de nomsOrganizationavec deux membres : une propriétéName(string) et une méthodeGetSalary()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émentsIOrgComponent. Incluez une méthodeAdd(IOrgComponent component)pour ajouter des membres. Sa méthodeGetSalary()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 (
employeeoudepartment), suivi du nom et, pour les employés, de leur salaire. Pour les services, vous recevrez également le nom du service parent (ouroots’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
QALa sortie devrait être :
Engineering Total Salary: 13500Le 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()}");
}
}
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
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et UtilisateurLogique du système d'empruntLINQ pour la rechercheInterface Console / Interface d'administrationTests unitaires (NUnit/xUnit)Entraînez-vous par vous-même : Compilateur C# en ligne