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 patron de conception structurel qui vous permet de composer des objets dans des structures arborescentes et de traiter les objets individuels et les groupes d'objets de manière uniforme. Il est parfait pour représenter des hiérarchies partie-tout où vous souhaitez travailler à la fois avec des éléments uniques et des collections via la même interface.
Le patron 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 patron 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 les contenus doivent être traités de la même manière. Il simplifie le code client en éliminant le besoin de distinguer les éléments simples des éléments complexes.
Défi
FacileConstruisons un système d'organigramme en utilisant le motif Composite. Vous allez créer une structure où les employés individuels et les départements (qui contiennent des employés ou d'autres départements) peuvent être traités de manière uniforme, vous permettant 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(chaîne de caractères) et une méthodeGetSalary()qui retourne un entier. Cette interface représente le contrat commun que les employés individuels et les départements suivront.OrgClasses.cs: Créez deux classes dans le même espace de noms :Employee- une classe feuille représentant un travailleur individuel. Elle prend un nom et un salaire dans son constructeur et implémente l'interface en retournant son propre salaire.Department- une classe composite représentant un groupe. Elle prend un nom dans son constructeur et maintient une liste d'élémentsIOrgComponent. Incluez une méthodeAdd(IOrgComponent component)pour ajouter des membres. Sa méthodeGetSalary()doit retourner la somme des salaires de tous ses membres.
Program.cs: Construisez une structure organisationnelle basée sur l'entrée et calculez le salaire total. Vous créerez des départements et des employés, les imbriquerez de manière appropriée, et démontrerez comment le motif composite vous permet de traiter toute la hiérarchie de manière uniforme.
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, leur salaire. Pour les départements, vous recevrez également le nom du département parent (ouroots'il s'agit d'un département de premier niveau)
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 département racine avec le 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 département Engineering contient Alice (5000), Bob (4500), et le département QA. QA contient Charlie (4000). Lorsque vous appelez GetSalary() sur Engineering, il calcule récursivement le total de tous les composants imbriqués - démontrant ainsi comment le motif Composite vous permet de travailler avec des structures arborescentes complexes via une interface simple et uniforme !
Aide-mémoire
Le pattern Composite est un patron de structure qui compose des objets dans des structures en arbre et traite les objets individuels et les groupes de manière uniforme via la même interface. Il est idéal pour les hiérarchies partie-tout.
Le pattern comporte trois composants clés :
- Interface composant : Définit les opérations communes
- Classes feuilles : Représentent les objets individuels
- Classes composites : Contiennent des enfants et implémentent la même interface
Exemple d'implémentation de base :
// Interface composant
public interface IFileSystemItem
{
string Name { get; }
int GetSize();
}
// Classe feuille
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;
}
// Classe composite
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());
}Utilisation - les clients travaillent avec des éléments uniques et des collections de manière uniforme :
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 pattern Composite est utile pour les systèmes de fichiers, les arbres de composants d'interface utilisateur, les organigrammes ou toute structure où les conteneurs et les contenus doivent être traités de la même manière.
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 Utilisateur