Menu
Coddy logo textTech

Pattern Composito

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 61 di 70.

Il pattern Composite è un pattern strutturale che consente di comporre oggetti in strutture ad albero e di trattare in modo uniforme gli oggetti singoli e i gruppi di oggetti. È perfetto per rappresentare gerarchie parte-tutto in cui vuoi lavorare sia con elementi singoli sia con raccolte tramite la stessa interfaccia.

Il pattern ha tre componenti chiave: un'interfaccia componente che definisce operazioni comuni, classi foglia che rappresentano singoli oggetti e classi composite che contengono figli e implementano la stessa interfaccia:

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());
}

Il bello di questo schema è che i client non devono sapere se stanno lavorando con un singolo file o con un'intera gerarchia di cartelle:

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

Il pattern Composite è ideale per i file system, gli alberi di componenti dell’interfaccia utente, gli organigrammi o qualsiasi struttura in cui i contenitori e i contenuti debbano essere trattati allo stesso modo. Semplifica il codice client eliminando la necessità di distinguere tra elementi semplici e complessi.

challenge icon

Sfida

Facile

Costruiamo un sistema di organigramma usando il pattern Composite. Creerai una struttura in cui sia i singoli dipendenti sia i reparti (che contengono dipendenti o altri reparti) possono essere trattati in modo uniforme, permettendoti di calcolare il costo totale degli stipendi a qualsiasi livello della gerarchia.

Organizzerai il codice in tre file:

  • OrgComponent.cs: definisci un'interfaccia IOrgComponent nello spazio dei nomi Organization con due membri: una proprietà Name (stringa) e un metodo GetSalary() che restituisce un intero. Questa interfaccia rappresenta il contratto comune seguito sia dai singoli dipendenti sia dai reparti.
  • OrgClasses.cs: crea due classi nello stesso spazio dei nomi:
    • Employee - una classe foglia che rappresenta un singolo lavoratore. Accetta un nome e uno stipendio nel costruttore e implementa l'interfaccia restituendo il proprio stipendio.
    • Department - una classe composita che rappresenta un gruppo. Accetta un nome nel costruttore e mantiene un elenco di elementi IOrgComponent. Includi un metodo Add(IOrgComponent component) per aggiungere membri. Il suo metodo GetSalary() deve restituire la somma degli stipendi di tutti i suoi membri.
  • Program.cs: crea una struttura organizzativa basata sull'input e calcola lo stipendio totale. Creerai reparti e dipendenti, li anniderai in modo appropriato e dimostrerai come il pattern Composite ti permette di trattare uniformemente l'intera gerarchia.

Riceverai i seguenti input:

  • Il numero di componenti da creare
  • Per ogni componente: il tipo (employee o department), seguito dal nome e, per i dipendenti, dal loro stipendio. Per i reparti, riceverai anche il nome del reparto genitore (o root se si tratta di un reparto di primo livello)

Formato dell'input per ogni componente:

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Dopo aver creato la struttura, stampa lo stipendio totale del reparto radice nel formato {root_name} Total Salary: {amount}.

Ad esempio, se gli input sono:

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

L'output dovrebbe essere:

Engineering Total Salary: 13500

Il reparto Engineering contiene Alice (5000), Bob (4500) e il reparto QA. QA contiene Charlie (4000). Quando chiami GetSalary() su Engineering, il totale viene calcolato ricorsivamente a partire da tutti i componenti annidati: dimostrazione di come il pattern Composite ti permetta di lavorare con strutture ad albero complesse tramite un'interfaccia semplice e uniforme!

Provalo tu

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

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Crea un dizionario per memorizzare i dipartimenti per nome
        // Questo ti aiuterà a trovare i dipartimenti genitori quando aggiungi i componenti
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO: Tieni traccia del dipartimento radice
        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: Crea il dipartimento
                // TODO: Aggiungilo al dizionario departments
                // TODO: Se parent è "root", questo è il dipartimento radice
                // TODO: Altrimenti, trova il dipartimento genitore e aggiungici questo dipartimento
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO: Crea il dipendente
                // TODO: Trova il dipartimento genitore e aggiungici il dipendente
            }
        }
        
        // TODO: Stampa lo stipendio totale del dipartimento radice
        // Formato: "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online